changeset 802:3428c60d7cfc

replace jetty jars with source
author Franklin Schmidt <fschmidt@gmail.com>
date Wed, 07 Sep 2016 21:15:48 -0600
parents 6a21393191c1
children 166b16bda630
files lib/jetty-continuation-8.1.15.v20140411.jar lib/jetty-http-8.1.15.v20140411.jar lib/jetty-io-8.1.15.v20140411.jar lib/jetty-server-8.1.15.v20140411.jar lib/jetty-util-8.1.15.v20140411.jar src/org/eclipse/jetty/continuation/Continuation.java src/org/eclipse/jetty/continuation/ContinuationFilter.java src/org/eclipse/jetty/continuation/ContinuationListener.java src/org/eclipse/jetty/continuation/ContinuationSupport.java src/org/eclipse/jetty/continuation/ContinuationThrowable.java src/org/eclipse/jetty/continuation/FauxContinuation.java src/org/eclipse/jetty/favicon.ico src/org/eclipse/jetty/http/AbstractGenerator.java src/org/eclipse/jetty/http/EncodedHttpURI.java src/org/eclipse/jetty/http/Generator.java src/org/eclipse/jetty/http/HttpBuffers.java src/org/eclipse/jetty/http/HttpBuffersImpl.java src/org/eclipse/jetty/http/HttpContent.java src/org/eclipse/jetty/http/HttpCookie.java src/org/eclipse/jetty/http/HttpException.java src/org/eclipse/jetty/http/HttpFields.java src/org/eclipse/jetty/http/HttpGenerator.java src/org/eclipse/jetty/http/HttpHeaderValues.java src/org/eclipse/jetty/http/HttpHeaders.java src/org/eclipse/jetty/http/HttpMethods.java src/org/eclipse/jetty/http/HttpParser.java src/org/eclipse/jetty/http/HttpSchemes.java src/org/eclipse/jetty/http/HttpStatus.java src/org/eclipse/jetty/http/HttpTokens.java src/org/eclipse/jetty/http/HttpURI.java src/org/eclipse/jetty/http/HttpVersions.java src/org/eclipse/jetty/http/MimeTypes.java src/org/eclipse/jetty/http/Parser.java src/org/eclipse/jetty/http/PathMap.java src/org/eclipse/jetty/http/encoding.properties src/org/eclipse/jetty/http/gzip/AbstractCompressedStream.java src/org/eclipse/jetty/http/gzip/CompressedResponseWrapper.java src/org/eclipse/jetty/http/mime.properties src/org/eclipse/jetty/http/ssl/SslContextFactory.java src/org/eclipse/jetty/http/useragents src/org/eclipse/jetty/io/AbstractBuffer.java src/org/eclipse/jetty/io/AbstractBuffers.java src/org/eclipse/jetty/io/AbstractConnection.java src/org/eclipse/jetty/io/AsyncEndPoint.java src/org/eclipse/jetty/io/Buffer.java src/org/eclipse/jetty/io/BufferCache.java src/org/eclipse/jetty/io/BufferDateCache.java src/org/eclipse/jetty/io/BufferUtil.java src/org/eclipse/jetty/io/Buffers.java src/org/eclipse/jetty/io/BuffersFactory.java src/org/eclipse/jetty/io/ByteArrayBuffer.java src/org/eclipse/jetty/io/ByteArrayEndPoint.java src/org/eclipse/jetty/io/ConnectedEndPoint.java src/org/eclipse/jetty/io/Connection.java src/org/eclipse/jetty/io/EndPoint.java src/org/eclipse/jetty/io/EofException.java src/org/eclipse/jetty/io/NetworkTrafficListener.java src/org/eclipse/jetty/io/PooledBuffers.java src/org/eclipse/jetty/io/RuntimeIOException.java src/org/eclipse/jetty/io/SimpleBuffers.java src/org/eclipse/jetty/io/ThreadLocalBuffers.java src/org/eclipse/jetty/io/UncheckedIOException.java src/org/eclipse/jetty/io/UncheckedPrintWriter.java src/org/eclipse/jetty/io/View.java src/org/eclipse/jetty/io/WriterOutputStream.java src/org/eclipse/jetty/io/bio/SocketEndPoint.java src/org/eclipse/jetty/io/bio/StreamEndPoint.java src/org/eclipse/jetty/io/bio/StringEndPoint.java src/org/eclipse/jetty/io/nio/AsyncConnection.java src/org/eclipse/jetty/io/nio/ChannelEndPoint.java src/org/eclipse/jetty/io/nio/DirectNIOBuffer.java src/org/eclipse/jetty/io/nio/IndirectNIOBuffer.java src/org/eclipse/jetty/io/nio/NIOBuffer.java src/org/eclipse/jetty/io/nio/NetworkTrafficSelectChannelEndPoint.java src/org/eclipse/jetty/io/nio/RandomAccessFileBuffer.java src/org/eclipse/jetty/io/nio/SelectChannelEndPoint.java src/org/eclipse/jetty/io/nio/SelectorManager.java src/org/eclipse/jetty/io/nio/SslConnection.java src/org/eclipse/jetty/server/AbstractConnector.java src/org/eclipse/jetty/server/AbstractHttpConnection.java src/org/eclipse/jetty/server/AsyncContinuation.java src/org/eclipse/jetty/server/AsyncHttpConnection.java src/org/eclipse/jetty/server/AsyncNCSARequestLog.java src/org/eclipse/jetty/server/Authentication.java src/org/eclipse/jetty/server/BlockingHttpConnection.java src/org/eclipse/jetty/server/Connector.java src/org/eclipse/jetty/server/CookieCutter.java src/org/eclipse/jetty/server/Dispatcher.java src/org/eclipse/jetty/server/Handler.java src/org/eclipse/jetty/server/HandlerContainer.java src/org/eclipse/jetty/server/HttpInput.java src/org/eclipse/jetty/server/HttpOutput.java src/org/eclipse/jetty/server/HttpWriter.java src/org/eclipse/jetty/server/InclusiveByteRange.java src/org/eclipse/jetty/server/LocalConnector.java src/org/eclipse/jetty/server/NCSARequestLog.java src/org/eclipse/jetty/server/Request.java src/org/eclipse/jetty/server/RequestLog.java src/org/eclipse/jetty/server/ResourceCache.java src/org/eclipse/jetty/server/Response.java src/org/eclipse/jetty/server/Server.java src/org/eclipse/jetty/server/ServletRequestHttpWrapper.java src/org/eclipse/jetty/server/ServletResponseHttpWrapper.java src/org/eclipse/jetty/server/SessionIdManager.java src/org/eclipse/jetty/server/SessionManager.java src/org/eclipse/jetty/server/ShutdownMonitor.java src/org/eclipse/jetty/server/UserIdentity.java src/org/eclipse/jetty/server/bio/SocketConnector.java src/org/eclipse/jetty/server/handler/AbstractHandler.java src/org/eclipse/jetty/server/handler/AbstractHandlerContainer.java src/org/eclipse/jetty/server/handler/ConnectHandler.java src/org/eclipse/jetty/server/handler/ContextHandler.java src/org/eclipse/jetty/server/handler/ContextHandlerCollection.java src/org/eclipse/jetty/server/handler/DebugHandler.java src/org/eclipse/jetty/server/handler/DefaultHandler.java src/org/eclipse/jetty/server/handler/ErrorHandler.java src/org/eclipse/jetty/server/handler/GzipHandler.java src/org/eclipse/jetty/server/handler/HandlerCollection.java src/org/eclipse/jetty/server/handler/HandlerList.java src/org/eclipse/jetty/server/handler/HandlerWrapper.java src/org/eclipse/jetty/server/handler/HotSwapHandler.java src/org/eclipse/jetty/server/handler/IPAccessHandler.java src/org/eclipse/jetty/server/handler/IdleTimeoutHandler.java src/org/eclipse/jetty/server/handler/MovedContextHandler.java src/org/eclipse/jetty/server/handler/ProxyHandler.java src/org/eclipse/jetty/server/handler/RequestLogHandler.java src/org/eclipse/jetty/server/handler/ResourceHandler.java src/org/eclipse/jetty/server/handler/ScopedHandler.java src/org/eclipse/jetty/server/handler/ShutdownHandler.java src/org/eclipse/jetty/server/handler/StatisticsHandler.java src/org/eclipse/jetty/server/nio/AbstractNIOConnector.java src/org/eclipse/jetty/server/nio/BlockingChannelConnector.java src/org/eclipse/jetty/server/nio/InheritedChannelConnector.java src/org/eclipse/jetty/server/nio/NIOConnector.java src/org/eclipse/jetty/server/nio/NetworkTrafficSelectChannelConnector.java src/org/eclipse/jetty/server/nio/SelectChannelConnector.java src/org/eclipse/jetty/server/nio/jmx/SelectChannelConnector-mbean.properties src/org/eclipse/jetty/server/session/AbstractSession.java src/org/eclipse/jetty/server/session/AbstractSessionIdManager.java src/org/eclipse/jetty/server/session/AbstractSessionManager.java src/org/eclipse/jetty/server/session/HashSessionIdManager.java src/org/eclipse/jetty/server/session/HashSessionManager.java src/org/eclipse/jetty/server/session/HashedSession.java src/org/eclipse/jetty/server/session/JDBCSessionIdManager.java src/org/eclipse/jetty/server/session/JDBCSessionManager.java src/org/eclipse/jetty/server/session/SessionHandler.java src/org/eclipse/jetty/server/ssl/ServletSSL.java src/org/eclipse/jetty/server/ssl/SslCertificates.java src/org/eclipse/jetty/server/ssl/SslConnector.java src/org/eclipse/jetty/server/ssl/SslSelectChannelConnector.java src/org/eclipse/jetty/server/ssl/SslSocketConnector.java src/org/eclipse/jetty/util/ArrayQueue.java src/org/eclipse/jetty/util/Atomics.java src/org/eclipse/jetty/util/Attributes.java src/org/eclipse/jetty/util/AttributesMap.java src/org/eclipse/jetty/util/B64Code.java src/org/eclipse/jetty/util/BlockingArrayQueue.java src/org/eclipse/jetty/util/ByteArrayISO8859Writer.java src/org/eclipse/jetty/util/ByteArrayOutputStream2.java src/org/eclipse/jetty/util/ConcurrentHashSet.java src/org/eclipse/jetty/util/DateCache.java src/org/eclipse/jetty/util/HostMap.java src/org/eclipse/jetty/util/IO.java src/org/eclipse/jetty/util/IPAddressMap.java src/org/eclipse/jetty/util/IntrospectionUtil.java src/org/eclipse/jetty/util/LazyList.java src/org/eclipse/jetty/util/Loader.java src/org/eclipse/jetty/util/MultiException.java src/org/eclipse/jetty/util/MultiMap.java src/org/eclipse/jetty/util/MultiPartInputStream.java src/org/eclipse/jetty/util/MultiPartOutputStream.java src/org/eclipse/jetty/util/MultiPartWriter.java src/org/eclipse/jetty/util/PatternMatcher.java src/org/eclipse/jetty/util/QuotedStringTokenizer.java src/org/eclipse/jetty/util/ReadLineInputStream.java src/org/eclipse/jetty/util/RolloverFileOutputStream.java src/org/eclipse/jetty/util/Scanner.java src/org/eclipse/jetty/util/StringMap.java src/org/eclipse/jetty/util/StringUtil.java src/org/eclipse/jetty/util/TypeUtil.java src/org/eclipse/jetty/util/URIUtil.java src/org/eclipse/jetty/util/UrlEncoded.java src/org/eclipse/jetty/util/Utf8Appendable.java src/org/eclipse/jetty/util/Utf8StringBuffer.java src/org/eclipse/jetty/util/Utf8StringBuilder.java src/org/eclipse/jetty/util/ajax/JSON.java src/org/eclipse/jetty/util/ajax/JSONCollectionConvertor.java src/org/eclipse/jetty/util/ajax/JSONDateConvertor.java src/org/eclipse/jetty/util/ajax/JSONEnumConvertor.java src/org/eclipse/jetty/util/ajax/JSONObjectConvertor.java src/org/eclipse/jetty/util/ajax/JSONPojoConvertor.java src/org/eclipse/jetty/util/ajax/JSONPojoConvertorFactory.java src/org/eclipse/jetty/util/component/AbstractLifeCycle.java src/org/eclipse/jetty/util/component/AggregateLifeCycle.java src/org/eclipse/jetty/util/component/Container.java src/org/eclipse/jetty/util/component/Destroyable.java src/org/eclipse/jetty/util/component/Dumpable.java src/org/eclipse/jetty/util/component/FileDestroyable.java src/org/eclipse/jetty/util/component/FileNoticeLifeCycleListener.java src/org/eclipse/jetty/util/component/LifeCycle.java src/org/eclipse/jetty/util/component/jmx/AggregateLifeCycle-mbean.properties src/org/eclipse/jetty/util/component/jmx/Dumpable-mbean.properties src/org/eclipse/jetty/util/component/jmx/LifeCycle-mbean.properties src/org/eclipse/jetty/util/log/AbstractLogger.java src/org/eclipse/jetty/util/log/JavaUtilLog.java src/org/eclipse/jetty/util/log/JettyAwareLogger.java src/org/eclipse/jetty/util/log/Log.java src/org/eclipse/jetty/util/log/Logger.java src/org/eclipse/jetty/util/log/LoggerLog.java src/org/eclipse/jetty/util/log/Slf4jLog.java src/org/eclipse/jetty/util/log/StdErrLog.java src/org/eclipse/jetty/util/preventers/AWTLeakPreventer.java src/org/eclipse/jetty/util/preventers/AbstractLeakPreventer.java src/org/eclipse/jetty/util/preventers/AppContextLeakPreventer.java src/org/eclipse/jetty/util/preventers/DOMLeakPreventer.java src/org/eclipse/jetty/util/preventers/DriverManagerLeakPreventer.java src/org/eclipse/jetty/util/preventers/GCThreadLeakPreventer.java src/org/eclipse/jetty/util/preventers/Java2DLeakPreventer.java src/org/eclipse/jetty/util/preventers/LDAPLeakPreventer.java src/org/eclipse/jetty/util/preventers/LoginConfigurationLeakPreventer.java src/org/eclipse/jetty/util/preventers/SecurityProviderLeakPreventer.java src/org/eclipse/jetty/util/resource/BadResource.java src/org/eclipse/jetty/util/resource/FileResource.java src/org/eclipse/jetty/util/resource/JarFileResource.java src/org/eclipse/jetty/util/resource/JarResource.java src/org/eclipse/jetty/util/resource/Resource.java src/org/eclipse/jetty/util/resource/ResourceCollection.java src/org/eclipse/jetty/util/resource/ResourceFactory.java src/org/eclipse/jetty/util/resource/URLResource.java src/org/eclipse/jetty/util/security/B64Code.java src/org/eclipse/jetty/util/security/CertificateUtils.java src/org/eclipse/jetty/util/security/CertificateValidator.java src/org/eclipse/jetty/util/security/Constraint.java src/org/eclipse/jetty/util/security/Credential.java src/org/eclipse/jetty/util/security/Password.java src/org/eclipse/jetty/util/security/UnixCrypt.java src/org/eclipse/jetty/util/ssl/AliasedX509ExtendedKeyManager.java src/org/eclipse/jetty/util/ssl/AliasedX509KeyManager.java src/org/eclipse/jetty/util/ssl/SslContextFactory.java src/org/eclipse/jetty/util/statistic/CounterStatistic.java src/org/eclipse/jetty/util/statistic/SampleStatistic.java src/org/eclipse/jetty/util/thread/ExecutorThreadPool.java src/org/eclipse/jetty/util/thread/QueuedThreadPool.java src/org/eclipse/jetty/util/thread/ShutdownThread.java src/org/eclipse/jetty/util/thread/ThreadPool.java src/org/eclipse/jetty/util/thread/Timeout.java src/org/eclipse/jetty/util/thread/jmx/QueuedThreadPool-mbean.properties src/org/eclipse/jetty/util/thread/jmx/ThreadPool-mbean.properties
diffstat 247 files changed, 77944 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
Binary file lib/jetty-continuation-8.1.15.v20140411.jar has changed
Binary file lib/jetty-http-8.1.15.v20140411.jar has changed
Binary file lib/jetty-io-8.1.15.v20140411.jar has changed
Binary file lib/jetty-server-8.1.15.v20140411.jar has changed
Binary file lib/jetty-util-8.1.15.v20140411.jar has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/continuation/Continuation.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,420 @@
+//
+//  ========================================================================
+//  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.continuation;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.Servlet;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.ServletResponseWrapper;
+
+/* ------------------------------------------------------------ */
+/**
+ * Continuation.
+ * 
+ * A continuation is a mechanism by which a HTTP Request can be suspended and
+ * restarted after a timeout or an asynchronous event has occurred.
+ * <p>
+ * The continuation mechanism is a portable mechanism that will work 
+ * asynchronously without additional configuration of all jetty-7, 
+ * jetty-8 and Servlet 3.0 containers.   With the addition of 
+ * the {@link ContinuationFilter}, the mechanism will also work
+ * asynchronously on jetty-6 and non-asynchronously on any 
+ * servlet 2.5 container.
+ * <p>
+ * The Continuation API is a simplification of the richer async API
+ * provided by the servlet-3.0 and an enhancement of the continuation
+ * API that was introduced with jetty-6. 
+ * </p>
+ * <h1>Continuation Usage</h1>
+ * <p>
+ * A continuation object is obtained for a request by calling the 
+ * factory method {@link ContinuationSupport#getContinuation(ServletRequest)}.
+ * The continuation type returned will depend on the servlet container
+ * being used.
+ * </p> 
+ * <p>
+ * There are two distinct style of operation of the continuation API.
+ * </p>
+ * <h3>Suspend/Resume Usage</h3> 
+ * <p>The suspend/resume style is used when a servlet and/or
+ * filter is used to generate the response after a asynchronous wait that is
+ * terminated by an asynchronous handler.
+ * </p>
+ * <pre>
+ * <b>Filter/Servlet:</b>
+ *   // if we need to get asynchronous results
+ *   Object results = request.getAttribute("results);
+ *   if (results==null)
+ *   {
+ *     Continuation continuation = ContinuationSupport.getContinuation(request);
+ *     continuation.suspend();
+ *     myAsyncHandler.register(continuation);
+ *     return; // or continuation.undispatch();
+ *   }
+ * 
+ * async wait ...
+ * 
+ * <b>Async Handler:</b>
+ *   // when the waited for event happens
+ *   continuation.setAttribute("results",event);
+ *   continuation.resume();
+ *   
+ * <b>Filter/Servlet:</b>
+ *   // when the request is redispatched 
+ *   if (results==null)
+ *   {
+ *     ... // see above
+ *   }
+ *   else
+ *   {
+ *     response.getOutputStream().write(process(results));
+ *   }
+ * </pre> 
+ * <h3>Suspend/Complete Usage</h3> 
+ * <p>
+ * The suspend/complete style is used when an asynchronous handler is used to 
+ * generate the response:
+ * </p>
+ * <pre>
+ * <b>Filter/Servlet:</b>
+ *   // when we want to enter asynchronous mode
+ *   Continuation continuation = ContinuationSupport.getContinuation(request);
+ *   continuation.suspend(response); // response may be wrapped
+ *   myAsyncHandler.register(continuation);
+ *   return; // or continuation.undispatch();
+ *
+ * <b>Wrapping Filter:</b>
+ *   // any filter that had wrapped the response should be implemented like:
+ *   try
+ *   {
+ *     chain.doFilter(request,wrappedResponse);
+ *   }
+ *   finally
+ *   {
+ *     if (!continuation.isResponseWrapped())
+ *       wrappedResponse.finish()
+ *     else
+ *       continuation.addContinuationListener(myCompleteListener)
+ *   }
+ *
+ * async wait ...
+ *
+ * <b>Async Handler:</b>
+ *   // when the async event happens
+ *   continuation.getServletResponse().getOutputStream().write(process(event));
+ *   continuation.complete()
+ * </pre>
+ * 
+ * <h1>Continuation Timeout</h1>
+ * <p>
+ * If a continuation is suspended, but neither {@link #complete()} or {@link #resume()} is
+ * called during the period set by {@link #setTimeout(long)}, then the continuation will
+ * expire and {@link #isExpired()} will return true. 
+ * </p>
+ * <p>
+ * When a continuation expires, the {@link ContinuationListener#onTimeout(Continuation)}
+ * method is called on any {@link ContinuationListener} that has been registered via the
+ * {@link #addContinuationListener(ContinuationListener)} method. The onTimeout handlers 
+ * may write a response and call {@link #complete()}. If {@link #complete()} is not called, 
+ * then the container will redispatch the request as if {@link #resume()} had been called,
+ * except that {@link #isExpired()} will be true and {@link #isResumed()} will be false.
+ * </p>
+ * 
+ * @see ContinuationSupport
+ * @see ContinuationListener
+ * 
+ */
+public interface Continuation
+{
+    public final static String ATTRIBUTE = "org.eclipse.jetty.continuation";
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set the continuation timeout.
+     * 
+     * @param timeoutMs
+     *            The time in milliseconds to wait before expiring this
+     *            continuation after a call to {@link #suspend()} or {@link #suspend(ServletResponse)}.
+     *            A timeout of <=0 means the continuation will never expire.
+     */
+    void setTimeout(long timeoutMs);
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Suspend the processing of the request and associated
+     * {@link ServletResponse}.
+     * 
+     * <p>
+     * After this method has been called, the lifecycle of the request will be
+     * extended beyond the return to the container from the
+     * {@link Servlet#service(ServletRequest, ServletResponse)} method and
+     * {@link Filter#doFilter(ServletRequest, ServletResponse, FilterChain)}
+     * calls. When a suspended request is returned to the container after
+     * a dispatch, then the container will not commit the associated response
+     * (unless an exception other than {@link ContinuationThrowable} is thrown).
+     * </p>
+     * 
+     * <p>
+     * When the thread calling the filter chain and/or servlet has returned to
+     * the container with a suspended request, the thread is freed for other
+     * tasks and the request is held until either:
+     * <ul>
+     * <li>a call to {@link #resume()}.</li>
+     * <li>a call to {@link #complete()}.</li>
+     * <li>the timeout expires.</li>
+     * </ul>
+     * <p>
+     * Typically suspend with no arguments is uses when a call to {@link #resume()}
+     * is expected. If a call to {@link #complete()} is expected, then the 
+     * {@link #suspend(ServletResponse)} method should be used instead of this method.
+     * </p>
+     * 
+     * @exception IllegalStateException
+     *                If the request cannot be suspended
+     */
+    void suspend();
+    
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Suspend the processing of the request and associated
+     * {@link ServletResponse}.
+     * 
+     * <p>
+     * After this method has been called, the lifecycle of the request will be
+     * extended beyond the return to the container from the
+     * {@link Servlet#service(ServletRequest, ServletResponse)} method and
+     * {@link Filter#doFilter(ServletRequest, ServletResponse, FilterChain)}
+     * calls. When a suspended request is returned to the container after
+     * a dispatch, then the container will not commit the associated response
+     * (unless an exception other than {@link ContinuationThrowable} is thrown).
+     * </p>
+     * <p>
+     * When the thread calling the filter chain and/or servlet has returned to
+     * the container with a suspended request, the thread is freed for other
+     * tasks and the request is held until either:
+     * <ul>
+     * <li>a call to {@link #resume()}.</li>
+     * <li>a call to {@link #complete()}.</li>
+     * <li>the timeout expires.</li>
+     * </ul>
+     * <p>
+     * Typically suspend with a response argument is uses when a call to {@link #complete()}
+     * is expected. If a call to {@link #resume()} is expected, then the 
+     * {@link #suspend()} method should be used instead of this method.
+     * </p>
+     * <p>
+     * Filters that may wrap the response object should check {@link #isResponseWrapped()}
+     * to decide if they should destroy/finish the wrapper. If {@link #isResponseWrapped()}
+     * returns true, then the wrapped request has been passed to the asynchronous
+     * handler and the wrapper should not be destroyed/finished until after a call to 
+     * {@link #complete()} (potentially using a {@link ContinuationListener#onComplete(Continuation)}
+     * listener).
+     * 
+     * @param response The response to return via a call to {@link #getServletResponse()}
+     * @exception IllegalStateException
+     *                If the request cannot be suspended
+     */
+    void suspend(ServletResponse response);
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Resume a suspended request.
+     * 
+     * <p>
+     * This method can be called by any thread that has been passed a reference
+     * to a continuation. When called the request is redispatched to the
+     * normal filter chain and servlet processing with {@link #isInitial()} false.
+     * </p>
+     * <p>
+     * If resume is called before a suspended request is returned to the
+     * container (ie the thread that called {@link #suspend()} is still
+     * within the filter chain and/or servlet service method), then the resume
+     * does not take effect until the call to the filter chain and/or servlet
+     * returns to the container. In this case both {@link #isSuspended()} and
+     * {@link #isResumed()} return true. Multiple calls to resume are ignored.
+     * </p>
+     * <p>
+     * Typically resume() is used after a call to {@link #suspend()} with
+     * no arguments. The dispatch after a resume call will use the original
+     * request and response objects, even if {@link #suspend(ServletResponse)} 
+     * had been passed a wrapped response.
+     * </p>
+     * 
+     * @see #suspend()
+     * @exception IllegalStateException if the request is not suspended.
+     * 
+     */
+    void resume();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Complete a suspended request.
+     * 
+     * <p>
+     * This method can be called by any thread that has been passed a reference
+     * to a suspended request. When a request is completed, the associated
+     * response object committed and flushed. The request is not redispatched.
+     * </p>
+     * 
+     * <p>
+     * If complete is called before a suspended request is returned to the
+     * container (ie the thread that called {@link #suspend()} is still
+     * within the filter chain and/or servlet service method), then the complete
+     * does not take effect until the call to the filter chain and/or servlet
+     * returns to the container. In this case both {@link #isSuspended()} and
+     * {@link #isResumed()} return true.
+     * </p>
+     * 
+     * <p>
+     * Typically resume() is used after a call to {@link #suspend(ServletResponse)} with
+     * a possibly wrapped response. The async handler should use the response
+     * provided by {@link #getServletResponse()} to write the response before
+     * calling {@link #complete()}. If the request was suspended with a 
+     * call to {@link #suspend()} then no response object will be available via
+     * {@link #getServletResponse()}.
+     * </p>
+     * 
+     * <p>
+     * Once complete has been called and any thread calling the filter chain
+     * and/or servlet chain has returned to the container, the request lifecycle
+     * is complete. The container is able to recycle request objects, so it is
+     * not valid hold a request or continuation reference after the end of the 
+     * life cycle.
+     * 
+     * @see #suspend()
+     * @exception IllegalStateException
+     *                if the request is not suspended.
+     * 
+     */
+    void complete();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return true after {@link #suspend()} has been called and before the
+     *         request has been redispatched due to being resumed, completed or
+     *         timed out.
+     */
+    boolean isSuspended();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return true if the request has been redispatched by a call to
+     *         {@link #resume()}. Returns false after any subsequent call to
+     *         suspend
+     */
+    boolean isResumed();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return true after a request has been redispatched as the result of a
+     *         timeout. Returns false after any subsequent call to suspend.
+     */
+    boolean isExpired();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return true while the request is within the initial dispatch to the
+     *         filter chain and/or servlet. Will return false once the calling
+     *         thread has returned to the container after suspend has been
+     *         called and during any subsequent redispatch.
+     */
+    boolean isInitial();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Is the suspended response wrapped.
+     * <p>
+     * Filters that wrap the response object should check this method to 
+     * determine if they should destroy/finish the wrapped response. If 
+     * the request was suspended with a call to {@link #suspend(ServletResponse)}
+     * that passed the wrapped response, then the filter should register
+     * a {@link ContinuationListener} to destroy/finish the wrapped response
+     * during a call to {@link ContinuationListener#onComplete(Continuation)}.
+     * @return True if {@link #suspend(ServletResponse)} has been passed a
+     * {@link ServletResponseWrapper} instance.
+     */
+    boolean isResponseWrapped();
+
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Get the suspended response.
+     * @return the {@link ServletResponse} passed to {@link #suspend(ServletResponse)}.
+     */
+    ServletResponse getServletResponse();
+    
+    /* ------------------------------------------------------------ */
+    /** 
+     * Add a ContinuationListener.
+     * 
+     * @param listener
+     */
+    void addContinuationListener(ContinuationListener listener);
+    
+    /* ------------------------------------------------------------ */
+    /** Set a request attribute.
+     * This method is a convenience method to call the {@link ServletRequest#setAttribute(String, Object)}
+     * method on the associated request object.
+     * This is a thread safe call and may be called by any thread.
+     * @param name the attribute name
+     * @param attribute the attribute value
+     */
+    public void setAttribute(String name, Object attribute);
+    
+    /* ------------------------------------------------------------ */
+    /** Get a request attribute.
+     * This method is a convenience method to call the {@link ServletRequest#getAttribute(String)}
+     * method on the associated request object.
+     * This is a thread safe call and may be called by any thread.
+     * @param name the attribute name
+     * @return the attribute value
+     */
+    public Object getAttribute(String name);
+    
+    /* ------------------------------------------------------------ */
+    /** Remove a request attribute.
+     * This method is a convenience method to call the {@link ServletRequest#removeAttribute(String)}
+     * method on the associated request object.
+     * This is a thread safe call and may be called by any thread.
+     * @param name the attribute name
+     */
+    public void removeAttribute(String name);
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Undispatch the request.
+     * <p>
+     * This method can be called on a suspended continuation in order
+     * to exit the dispatch to the filter/servlet by throwing a {@link ContinuationThrowable}
+     * which is caught either by the container or the {@link ContinuationFilter}.
+     * This is an alternative to simply returning from the dispatch in the case
+     * where filters in the filter chain may not be prepared to handle a suspended
+     * request.
+     * </p>
+     * This method should only be used as a last resort and a normal return is a prefereable
+     * solution if filters can be updated to handle that case.
+     * 
+     * @throws ContinuationThrowable thrown if the request is suspended. The instance of the 
+     * exception may be reused on subsequent calls, so the stack frame may not be accurate.
+     */
+    public void undispatch() throws ContinuationThrowable;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/continuation/ContinuationFilter.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,174 @@
+//
+//  ========================================================================
+//  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.continuation;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+
+
+/* ------------------------------------------------------------ */
+/**
+ * <p>ContinuationFilter must be applied to servlet paths that make use of
+ * the asynchronous features provided by {@link Continuation} APIs, but that
+ * are deployed in servlet containers that are neither Jetty (>= 7) nor a
+ * compliant Servlet 3.0 container.</p>
+ * <p>The following init parameters may be used to configure the filter (these are mostly for testing):</p>
+ * <dl>
+ * <dt>debug</dt><dd>Boolean controlling debug output</dd>
+ * <dt>jetty6</dt><dd>Boolean to force use of Jetty 6 continuations</dd>
+ * <dt>faux</dt><dd>Boolean to force use of faux continuations</dd>
+ * </dl>
+ * <p>If the servlet container is not Jetty (either 6 or 7) nor a Servlet 3
+ * container, then "faux" continuations will be used.</p>
+ * <p>Faux continuations will just put the thread that called {@link Continuation#suspend()}
+ * in wait, and will notify that thread when {@link Continuation#resume()} or
+ * {@link Continuation#complete()} is called.</p>
+ * <p>Faux continuations are not threadless continuations (they are "faux" - fake - for this reason)
+ * and as such they will scale less than proper continuations.</p>
+ */
+public class ContinuationFilter implements Filter
+{
+    static boolean _initialized;
+    static boolean __debug; // shared debug status
+    private boolean _faux;
+    private boolean _jetty6;
+    private boolean _filtered;
+    ServletContext _context;
+    private boolean _debug;
+
+    public void init(FilterConfig filterConfig) throws ServletException
+    {
+        boolean jetty_7_or_greater="org.eclipse.jetty.servlet".equals(filterConfig.getClass().getPackage().getName());
+        _context = filterConfig.getServletContext();
+
+        String param=filterConfig.getInitParameter("debug");
+        _debug=param!=null&&Boolean.parseBoolean(param);
+        if (_debug)
+            __debug=true;
+
+        param=filterConfig.getInitParameter("jetty6");
+        if (param==null)
+            param=filterConfig.getInitParameter("partial");
+        if (param!=null)
+            _jetty6=Boolean.parseBoolean(param);
+        else
+            _jetty6=ContinuationSupport.__jetty6 && !jetty_7_or_greater;
+
+        param=filterConfig.getInitParameter("faux");
+        if (param!=null)
+            _faux=Boolean.parseBoolean(param);
+        else
+            _faux=!(jetty_7_or_greater || _jetty6 || _context.getMajorVersion()>=3);
+
+        _filtered=_faux||_jetty6;
+        if (_debug)
+            _context.log("ContinuationFilter "+
+                    " jetty="+jetty_7_or_greater+
+                    " jetty6="+_jetty6+
+                    " faux="+_faux+
+                    " filtered="+_filtered+
+                    " servlet3="+ContinuationSupport.__servlet3);
+        _initialized=true;
+    }
+
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
+    {
+        if (_filtered)
+        {
+            Continuation c = (Continuation) request.getAttribute(Continuation.ATTRIBUTE);
+            FilteredContinuation fc;
+            if (_faux && (c==null || !(c instanceof FauxContinuation)))
+            {
+                fc = new FauxContinuation(request);
+                request.setAttribute(Continuation.ATTRIBUTE,fc);
+            }
+            else
+                fc=(FilteredContinuation)c;
+
+            boolean complete=false;
+            while (!complete)
+            {
+                try
+                {
+                    if (fc==null || (fc).enter(response))
+                        chain.doFilter(request,response);
+                }
+                catch (ContinuationThrowable e)
+                {
+                    debug("faux",e);
+                }
+                finally
+                {
+                    if (fc==null)
+                        fc = (FilteredContinuation) request.getAttribute(Continuation.ATTRIBUTE);
+
+                    complete=fc==null || (fc).exit();
+                }
+            }
+        }
+        else
+        {
+            try
+            {
+                chain.doFilter(request,response);
+            }
+            catch (ContinuationThrowable e)
+            {
+                debug("caught",e);
+            }
+        }
+    }
+
+    private void debug(String string)
+    {
+        if (_debug)
+        {
+            _context.log(string);
+        }
+    }
+
+    private void debug(String string, Throwable th)
+    {
+        if (_debug)
+        {
+            if (th instanceof ContinuationThrowable)
+                _context.log(string+":"+th);
+            else
+                _context.log(string,th);
+        }
+    }
+
+    public void destroy()
+    {
+    }
+
+    public interface FilteredContinuation extends Continuation
+    {
+        boolean enter(ServletResponse response);
+        boolean exit();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/continuation/ContinuationListener.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,55 @@
+//
+//  ========================================================================
+//  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.continuation;
+
+import java.util.EventListener;
+
+import javax.servlet.ServletRequestListener;
+
+
+/* ------------------------------------------------------------ */
+/** A Continuation Listener
+ * <p>
+ * A ContinuationListener may be registered with a call to
+ * {@link Continuation#addContinuationListener(ContinuationListener)}.
+ * 
+ */
+public interface ContinuationListener extends EventListener 
+{    
+    /* ------------------------------------------------------------ */
+    /**
+     * Called when a continuation life cycle is complete and after
+     * any calls to {@link ServletRequestListener#requestDestroyed(javax.servlet.ServletRequestEvent)}
+     * The response may still be written to during the call.
+     * 
+     * @param continuation
+     */
+    public void onComplete(Continuation continuation);
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Called when a suspended continuation has timed out.
+     * The response may be written to and the methods 
+     * {@link Continuation#resume()} or {@link Continuation#complete()} 
+     * may be called by a onTimeout implementation,
+     * @param continuation
+     */
+    public void onTimeout(Continuation continuation);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/continuation/ContinuationSupport.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,164 @@
+//
+//  ========================================================================
+//  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.continuation;
+
+import java.lang.reflect.Constructor;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletRequestWrapper;
+import javax.servlet.ServletResponse;
+
+/* ------------------------------------------------------------ */
+/** ContinuationSupport.
+ *
+ * Factory class for accessing Continuation instances, which with either be
+ * native to the container (jetty >= 6), a servlet 3.0 or a faux continuation.
+ *
+ */
+public class ContinuationSupport
+{
+    static final boolean __jetty6;
+    static final boolean __servlet3;
+    static final Class<?> __waitingContinuation;
+    static final Constructor<? extends Continuation> __newServlet3Continuation;
+    static final Constructor<? extends Continuation> __newJetty6Continuation;
+    static
+    {
+        boolean servlet3Support=false;
+        Constructor<? extends Continuation>s3cc=null;
+        try
+        {
+            boolean servlet3=ServletRequest.class.getMethod("startAsync")!=null;
+            if (servlet3)
+            {
+                Class<? extends Continuation> s3c = ContinuationSupport.class.getClassLoader().loadClass("org.eclipse.jetty.continuation.Servlet3Continuation").asSubclass(Continuation.class);
+                s3cc=s3c.getConstructor(ServletRequest.class);
+                servlet3Support=true;
+            }
+        }
+        catch (Exception e)
+        {}
+        finally
+        {
+            __servlet3=servlet3Support;
+            __newServlet3Continuation=s3cc;
+        }
+
+        boolean jetty6Support=false;
+        Constructor<? extends Continuation>j6cc=null;
+        try
+        {
+            Class<?> jetty6ContinuationClass = ContinuationSupport.class.getClassLoader().loadClass("org.mortbay.util.ajax.Continuation");
+            boolean jetty6=jetty6ContinuationClass!=null;
+            if (jetty6)
+            {
+                Class<? extends Continuation> j6c = ContinuationSupport.class.getClassLoader().loadClass("org.eclipse.jetty.continuation.Jetty6Continuation").asSubclass(Continuation.class);
+                j6cc=j6c.getConstructor(ServletRequest.class, jetty6ContinuationClass);
+                jetty6Support=true;
+            }
+        }
+        catch (Exception e)
+        {}
+        finally
+        {
+            __jetty6=jetty6Support;
+            __newJetty6Continuation=j6cc;
+        }
+
+        Class<?> waiting=null;
+        try
+        {
+            waiting=ContinuationSupport.class.getClassLoader().loadClass("org.mortbay.util.ajax.WaitingContinuation");
+        }
+        catch (Exception e)
+        {
+        }
+        finally
+        {
+            __waitingContinuation=waiting;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Get a Continuation.  The type of the Continuation returned may
+     * vary depending on the container in which the application is
+     * deployed. It may be an implementation native to the container (eg
+     * org.eclipse.jetty.server.AsyncContinuation) or one of the utility
+     * implementations provided such as an internal <code>FauxContinuation</code>
+     * or a real implementation like {@link org.eclipse.jetty.continuation.Servlet3Continuation}.
+     * @param request The request
+     * @return a Continuation instance
+     */
+    public static Continuation getContinuation(ServletRequest request)
+    {
+        Continuation continuation = (Continuation) request.getAttribute(Continuation.ATTRIBUTE);
+        if (continuation!=null)
+            return continuation;
+
+        while (request instanceof ServletRequestWrapper)
+            request=((ServletRequestWrapper)request).getRequest();
+
+        if (__servlet3 )
+        {
+            try
+            {
+                continuation=__newServlet3Continuation.newInstance(request);
+                request.setAttribute(Continuation.ATTRIBUTE,continuation);
+                return continuation;
+            }
+            catch(Exception e)
+            {
+                throw new RuntimeException(e);
+            }
+        }
+
+        if (__jetty6)
+        {
+            Object c=request.getAttribute("org.mortbay.jetty.ajax.Continuation");
+            try
+            {
+                if (c==null || __waitingContinuation==null || __waitingContinuation.isInstance(c))
+                    continuation=new FauxContinuation(request);
+                else
+                    continuation= __newJetty6Continuation.newInstance(request,c);
+                request.setAttribute(Continuation.ATTRIBUTE,continuation);
+                return continuation;
+            }
+            catch(Exception e)
+            {
+                throw new RuntimeException(e);
+            }
+        }
+
+        throw new IllegalStateException("!(Jetty || Servlet 3.0 || ContinuationFilter)");
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param request the servlet request
+     * @param response the servlet response
+     * @deprecated use {@link #getContinuation(ServletRequest)}
+     * @return the continuation
+     */
+    @Deprecated
+    public static Continuation getContinuation(final ServletRequest request, final ServletResponse response)
+    {
+        return getContinuation(request);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/continuation/ContinuationThrowable.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,46 @@
+//
+//  ========================================================================
+//  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.continuation;
+
+
+/* ------------------------------------------------------------ */
+/** ContinuationThrowable
+ * <p>
+ * A ContinuationThrowable is throw by {@link Continuation#undispatch()}
+ * in order to exit the dispatch to a Filter or Servlet.  Use of
+ * ContinuationThrowable is discouraged and it is preferable to 
+ * allow return to be used. ContinuationThrowables should only be
+ * used when there is a Filter/Servlet which cannot be modified
+ * to avoid committing a response when {@link Continuation#isSuspended()}
+ * is true.
+ * </p>
+ * <p>
+ * ContinuationThrowable instances are often reused so that the
+ * stack trace may be entirely unrelated to the calling stack.
+ * A real stack trace may be obtained by enabling debug.
+ * </p>
+ * <p>
+ * ContinuationThrowable extends Error as this is more likely
+ * to be uncaught (or rethrown) by a Filter/Servlet.  A ContinuationThrowable
+ * does not represent and error condition.
+ * </p>
+ */
+public class ContinuationThrowable extends Error
+{}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/continuation/FauxContinuation.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,508 @@
+//
+//  ========================================================================
+//  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.continuation;
+
+import java.util.ArrayList;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.ServletResponseWrapper;
+
+import org.eclipse.jetty.continuation.ContinuationFilter.FilteredContinuation;
+
+
+/* ------------------------------------------------------------ */
+/**
+ * A blocking implementation of Continuation.
+ * This implementation of Continuation is used by the {@link ContinuationFilter}
+ * when there are is no native or asynchronous continuation type available. 
+ */
+class FauxContinuation implements FilteredContinuation
+{
+    // common exception used for all continuations.  
+    // Turn on debug in ContinuationFilter to see real stack trace.
+    private final static ContinuationThrowable __exception = new ContinuationThrowable();
+    
+    private static final int __HANDLING=1;   // Request dispatched to filter/servlet
+    private static final int __SUSPENDING=2;   // Suspend called, but not yet returned to container
+    private static final int __RESUMING=3;     // resumed while suspending
+    private static final int __COMPLETING=4;   // resumed while suspending or suspended
+    private static final int __SUSPENDED=5;    // Suspended and parked
+    private static final int __UNSUSPENDING=6;
+    private static final int __COMPLETE=7;
+
+    private final ServletRequest _request;
+    private ServletResponse _response;
+    
+    private int _state=__HANDLING;
+    private boolean _initial=true;
+    private boolean _resumed=false;
+    private boolean _timeout=false;
+    private boolean _responseWrapped=false;
+    private  long _timeoutMs=30000; // TODO configure
+    
+    private ArrayList<ContinuationListener> _listeners; 
+
+    FauxContinuation(final ServletRequest request)
+    {
+        _request=request;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void onComplete()
+    {
+        if (_listeners!=null)
+            for (ContinuationListener l:_listeners)
+                l.onComplete(this);
+    }
+    
+    /* ------------------------------------------------------------ */
+    public void onTimeout()
+    {
+        if (_listeners!=null)
+            for (ContinuationListener l:_listeners)
+                l.onTimeout(this);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.continuation.Continuation#isResponseWrapped()
+     */
+    public boolean isResponseWrapped()
+    {
+        return _responseWrapped;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isInitial()
+    {
+        synchronized(this)
+        {
+            return _initial;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isResumed()
+    {
+        synchronized(this)
+        {
+            return _resumed;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isSuspended()
+    {
+        synchronized(this)
+        {
+            switch(_state)
+            {
+                case __HANDLING:
+                    return false;
+                case __SUSPENDING:
+                case __RESUMING:
+                case __COMPLETING:
+                case __SUSPENDED:
+                    return true;
+                case __UNSUSPENDING:
+                default:
+                    return false;   
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isExpired()
+    {
+        synchronized(this)
+        {
+            return _timeout;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setTimeout(long timeoutMs)
+    {
+        _timeoutMs = timeoutMs;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void suspend(ServletResponse response)
+    {
+        _response=response;
+        _responseWrapped=response instanceof ServletResponseWrapper;
+        suspend();
+    }
+    
+    /* ------------------------------------------------------------ */
+    public void suspend()
+    {
+        synchronized (this)
+        {
+            switch(_state)
+            {
+                case __HANDLING:
+                    _timeout=false;
+                    _resumed=false;
+                    _state=__SUSPENDING;
+                    return;
+
+                case __SUSPENDING:
+                case __RESUMING:
+                    return;
+
+                case __COMPLETING:
+                case __SUSPENDED:
+                case __UNSUSPENDING:
+                    throw new IllegalStateException(this.getStatusString());
+
+                default:
+                    throw new IllegalStateException(""+_state);
+            }
+
+        }
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /* (non-Javadoc)
+     * @see org.mortbay.jetty.Suspendor#resume()
+     */
+    public void resume()
+    {
+        synchronized (this)
+        {
+            switch(_state)
+            {
+                case __HANDLING:
+                    _resumed=true;
+                    return;
+                    
+                case __SUSPENDING:
+                    _resumed=true;
+                    _state=__RESUMING;
+                    return;
+
+                case __RESUMING:
+                case __COMPLETING:
+                    return;
+                    
+                case __SUSPENDED:
+                    fauxResume();
+                    _resumed=true;
+                    _state=__UNSUSPENDING;
+                    break;
+                    
+                case __UNSUSPENDING:
+                    _resumed=true;
+                    return;
+                    
+                default:
+                    throw new IllegalStateException(this.getStatusString());
+            }
+        }
+        
+    }
+    
+
+    /* ------------------------------------------------------------ */
+    public void complete()
+    {
+        // just like resume, except don't set _resumed=true;
+        synchronized (this)
+        {
+            switch(_state)
+            {
+                case __HANDLING:
+                    throw new IllegalStateException(this.getStatusString());
+                    
+                case __SUSPENDING:
+                    _state=__COMPLETING;
+                    break;
+                    
+                case __RESUMING:
+                    break;
+
+                case __COMPLETING:
+                    return;
+                    
+                case __SUSPENDED:
+                    _state=__COMPLETING;
+                    fauxResume();
+                    break;
+                    
+                case __UNSUSPENDING:
+                    return;
+                    
+                default:
+                    throw new IllegalStateException(this.getStatusString());
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.continuation.Continuation#getServletResponse()
+     */
+    public boolean enter(ServletResponse response)
+    {
+        _response=response;
+        return true;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.continuation.Continuation#getServletResponse()
+     */
+    public ServletResponse getServletResponse()
+    {
+        return _response;
+    }
+    
+
+    /* ------------------------------------------------------------ */
+    void handling()
+    {
+        synchronized (this)
+        {
+            _responseWrapped=false;
+            switch(_state)
+            {
+                case __HANDLING:
+                    throw new IllegalStateException(this.getStatusString());
+
+                case __SUSPENDING:
+                case __RESUMING:
+                    throw new IllegalStateException(this.getStatusString());
+
+                case __COMPLETING:
+                    return;
+
+                case __SUSPENDED:
+                    fauxResume();
+                case __UNSUSPENDING:
+                    _state=__HANDLING;
+                    return;
+
+                default:
+                    throw new IllegalStateException(""+_state);
+            }
+
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return true if handling is complete
+     */
+    public boolean exit()
+    {
+        synchronized (this)
+        {
+            switch(_state)
+            {
+                case __HANDLING:
+                    _state=__COMPLETE;
+                    onComplete();
+                    return true;
+
+                case __SUSPENDING:
+                    _initial=false;
+                    _state=__SUSPENDED;
+                    fauxSuspend(); // could block and change state.
+                    if (_state==__SUSPENDED || _state==__COMPLETING)
+                    {
+                        onComplete();
+                        return true;
+                    }
+                    
+                    _initial=false;
+                    _state=__HANDLING;
+                    return false; 
+
+                case __RESUMING:
+                    _initial=false;
+                    _state=__HANDLING;
+                    return false; 
+
+                case __COMPLETING:
+                    _initial=false;
+                    _state=__COMPLETE;
+                    onComplete();
+                    return true;
+
+                case __SUSPENDED:
+                case __UNSUSPENDING:
+                default:
+                    throw new IllegalStateException(this.getStatusString());
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    protected void expire()
+    {
+        // just like resume, except don't set _resumed=true;
+
+        synchronized (this)
+        {
+            _timeout=true;
+        }
+        
+        onTimeout();
+        
+        synchronized (this)
+        {
+            switch(_state)
+            {
+                case __HANDLING:
+                    return;
+                    
+                case __SUSPENDING:
+                    _timeout=true;
+                    _state=__RESUMING;
+                    fauxResume();
+                    return;
+                    
+                case __RESUMING:
+                    return;
+                    
+                case __COMPLETING:
+                    return;
+                    
+                case __SUSPENDED:
+                    _timeout=true;
+                    _state=__UNSUSPENDING;
+                    break;
+                    
+                case __UNSUSPENDING:
+                    _timeout=true;
+                    return;
+                    
+                default:
+                    throw new IllegalStateException(this.getStatusString());
+            }
+        }
+    }
+
+    private void fauxSuspend()
+    {
+        long expire_at = System.currentTimeMillis()+_timeoutMs;
+        long wait=_timeoutMs;
+        while (_timeoutMs>0 && wait>0)
+        {
+            try
+            {
+                this.wait(wait);
+            }
+            catch (InterruptedException e)
+            {
+                break;
+            }
+            wait=expire_at-System.currentTimeMillis();
+        }
+
+        if (_timeoutMs>0 && wait<=0)
+            expire();
+    }
+    
+    private void fauxResume()
+    {
+        _timeoutMs=0;
+        this.notifyAll();
+    }
+    
+    @Override
+    public String toString()
+    {
+        return getStatusString();
+    }
+    
+    String getStatusString()
+    {
+        synchronized (this)
+        {
+            return
+            ((_state==__HANDLING)?"HANDLING":
+                    (_state==__SUSPENDING)?"SUSPENDING":
+                        (_state==__SUSPENDED)?"SUSPENDED":
+                            (_state==__RESUMING)?"RESUMING":
+                                (_state==__UNSUSPENDING)?"UNSUSPENDING":
+                                    (_state==__COMPLETING)?"COMPLETING":
+                                    ("???"+_state))+
+            (_initial?",initial":"")+
+            (_resumed?",resumed":"")+
+            (_timeout?",timeout":"");
+        }
+    }
+
+    
+    public void addContinuationListener(ContinuationListener listener)
+    {
+        if (_listeners==null)
+            _listeners=new ArrayList<ContinuationListener>();
+        _listeners.add(listener);
+        
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.continuation.Continuation#getAttribute(java.lang.String)
+     */
+    public Object getAttribute(String name)
+    {
+        return _request.getAttribute(name);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.continuation.Continuation#removeAttribute(java.lang.String)
+     */
+    public void removeAttribute(String name)
+    {
+        _request.removeAttribute(name);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.continuation.Continuation#setAttribute(java.lang.String, java.lang.Object)
+     */
+    public void setAttribute(String name, Object attribute)
+    {
+        _request.setAttribute(name,attribute);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.continuation.Continuation#undispatch()
+     */
+    public void undispatch()
+    {
+        if (isSuspended())
+        {
+            if (ContinuationFilter.__debug)
+                throw new ContinuationThrowable();
+            throw __exception;
+        }
+        throw new IllegalStateException("!suspended");
+        
+    }
+}
Binary file src/org/eclipse/jetty/favicon.ico has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/http/AbstractGenerator.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,532 @@
+//
+//  ========================================================================
+//  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.http;
+
+import java.io.IOException;
+
+import org.eclipse.jetty.io.Buffer;
+import org.eclipse.jetty.io.Buffers;
+import org.eclipse.jetty.io.ByteArrayBuffer;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.EofException;
+import org.eclipse.jetty.io.View;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/* ------------------------------------------------------------ */
+/**
+ * Abstract Generator. Builds HTTP Messages.
+ *
+ * Currently this class uses a system parameter "jetty.direct.writers" to control
+ * two optional writer to byte conversions. buffer.writers=true will probably be
+ * faster, but will consume more memory.   This option is just for testing and tuning.
+ *
+ */
+public abstract class AbstractGenerator implements Generator
+{
+    private static final Logger LOG = Log.getLogger(AbstractGenerator.class);
+
+    // states
+    public final static int STATE_HEADER = 0;
+    public final static int STATE_CONTENT = 2;
+    public final static int STATE_FLUSHING = 3;
+    public final static int STATE_END = 4;
+
+    public static final byte[] NO_BYTES = {};
+
+    // data
+
+    protected final Buffers _buffers; // source of buffers
+    protected final EndPoint _endp;
+
+    protected int _state = STATE_HEADER;
+
+    protected int _status = 0;
+    protected int _version = HttpVersions.HTTP_1_1_ORDINAL;
+    protected  Buffer _reason;
+    protected  Buffer _method;
+    protected  String _uri;
+
+    protected long _contentWritten = 0;
+    protected long _contentLength = HttpTokens.UNKNOWN_CONTENT;
+    protected boolean _last = false;
+    protected boolean _head = false;
+    protected boolean _noContent = false;
+    protected Boolean _persistent = null;
+
+    protected Buffer _header; // Buffer for HTTP header (and maybe small _content)
+    protected Buffer _buffer; // Buffer for copy of passed _content
+    protected Buffer _content; // Buffer passed to addContent
+
+    protected Buffer _date;
+
+    private boolean _sendServerVersion;
+
+
+    /* ------------------------------------------------------------------------------- */
+    /**
+     * Constructor.
+     *
+     * @param buffers buffer pool
+     * @param io the end point
+     */
+    public AbstractGenerator(Buffers buffers, EndPoint io)
+    {
+        this._buffers = buffers;
+        this._endp = io;
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    public abstract boolean isRequest();
+
+    /* ------------------------------------------------------------------------------- */
+    public abstract boolean isResponse();
+
+    /* ------------------------------------------------------------------------------- */
+    public boolean isOpen()
+    {
+        return _endp.isOpen();
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    public void reset()
+    {
+        _state = STATE_HEADER;
+        _status = 0;
+        _version = HttpVersions.HTTP_1_1_ORDINAL;
+        _reason = null;
+        _last = false;
+        _head = false;
+        _noContent=false;
+        _persistent = null;
+        _contentWritten = 0;
+        _contentLength = HttpTokens.UNKNOWN_CONTENT;
+        _date = null;
+
+        _content = null;
+        _method=null;
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    public void returnBuffers()
+    {
+        if (_buffer!=null && _buffer.length()==0)
+        {
+            _buffers.returnBuffer(_buffer);
+            _buffer=null;
+        }
+
+        if (_header!=null && _header.length()==0)
+        {
+            _buffers.returnBuffer(_header);
+            _header=null;
+        }
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    public void resetBuffer()
+    {
+        if(_state>=STATE_FLUSHING)
+            throw new IllegalStateException("Flushed");
+
+        _last = false;
+        _persistent=null;
+        _contentWritten = 0;
+        _contentLength = HttpTokens.UNKNOWN_CONTENT;
+        _content=null;
+        if (_buffer!=null)
+            _buffer.clear();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the contentBufferSize.
+     */
+    public int getContentBufferSize()
+    {
+        if (_buffer==null)
+            _buffer=_buffers.getBuffer();
+        return _buffer.capacity();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param contentBufferSize The contentBufferSize to set.
+     */
+    public void increaseContentBufferSize(int contentBufferSize)
+    {
+        if (_buffer==null)
+            _buffer=_buffers.getBuffer();
+        if (contentBufferSize > _buffer.capacity())
+        {
+            Buffer nb = _buffers.getBuffer(contentBufferSize);
+            nb.put(_buffer);
+            _buffers.returnBuffer(_buffer);
+            _buffer = nb;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public Buffer getUncheckedBuffer()
+    {
+        return _buffer;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean getSendServerVersion ()
+    {
+        return _sendServerVersion;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setSendServerVersion (boolean sendServerVersion)
+    {
+        _sendServerVersion = sendServerVersion;
+    }
+
+    /* ------------------------------------------------------------ */
+    public int getState()
+    {
+        return _state;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isState(int state)
+    {
+        return _state == state;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isComplete()
+    {
+        return _state == STATE_END;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isIdle()
+    {
+        return _state == STATE_HEADER && _method==null && _status==0;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isCommitted()
+    {
+        return _state != STATE_HEADER;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the head.
+     */
+    public boolean isHead()
+    {
+        return _head;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setContentLength(long value)
+    {
+        if (value<0)
+            _contentLength=HttpTokens.UNKNOWN_CONTENT;
+        else
+            _contentLength=value;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param head The head to set.
+     */
+    public void setHead(boolean head)
+    {
+        _head = head;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return <code>false</code> if the connection should be closed after a request has been read,
+     * <code>true</code> if it should be used for additional requests.
+     */
+    public boolean isPersistent()
+    {
+        return _persistent!=null
+        ?_persistent.booleanValue()
+        :(isRequest()?true:_version>HttpVersions.HTTP_1_0_ORDINAL);
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setPersistent(boolean persistent)
+    {
+        _persistent=persistent;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param version The version of the client the response is being sent to (NB. Not the version
+     *            in the response, which is the version of the server).
+     */
+    public void setVersion(int version)
+    {
+        if (_state != STATE_HEADER)
+            throw new IllegalStateException("STATE!=START "+_state);
+        _version = version;
+        if (_version==HttpVersions.HTTP_0_9_ORDINAL && _method!=null)
+            _noContent=true;
+    }
+
+    /* ------------------------------------------------------------ */
+    public int getVersion()
+    {
+        return _version;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.http.Generator#setDate(org.eclipse.jetty.io.Buffer)
+     */
+    public void setDate(Buffer timeStampBuffer)
+    {
+        _date=timeStampBuffer;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     */
+    public void setRequest(String method, String uri)
+    {
+        if (method==null || HttpMethods.GET.equals(method) )
+            _method=HttpMethods.GET_BUFFER;
+        else
+            _method=HttpMethods.CACHE.lookup(method);
+        _uri=uri;
+        if (_version==HttpVersions.HTTP_0_9_ORDINAL)
+            _noContent=true;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param status The status code to send.
+     * @param reason the status message to send.
+     */
+    public void setResponse(int status, String reason)
+    {
+        if (_state != STATE_HEADER) throw new IllegalStateException("STATE!=START");
+        _method=null;
+        _status = status;
+        if (reason!=null)
+        {
+            int len=reason.length();
+
+            // TODO don't hard code
+            if (len>1024)
+                len=1024;
+            _reason=new ByteArrayBuffer(len);
+            for (int i=0;i<len;i++)
+            {
+                char ch = reason.charAt(i);
+                if (ch!='\r'&&ch!='\n')
+                    _reason.put((byte)ch);
+                else
+                    _reason.put((byte)' ');
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Prepare buffer for unchecked writes.
+     * Prepare the generator buffer to receive unchecked writes
+     * @return the available space in the buffer.
+     * @throws IOException
+     */
+    public abstract int prepareUncheckedAddContent() throws IOException;
+
+    /* ------------------------------------------------------------ */
+    void uncheckedAddContent(int b)
+    {
+        _buffer.put((byte)b);
+    }
+
+    /* ------------------------------------------------------------ */
+    public void completeUncheckedAddContent()
+    {
+        if (_noContent)
+        {
+            if(_buffer!=null)
+                _buffer.clear();
+        }
+        else
+        {
+            _contentWritten+=_buffer.length();
+            if (_head)
+                _buffer.clear();
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isBufferFull()
+    {
+        if (_buffer != null && _buffer.space()==0)
+        {
+            if (_buffer.length()==0 && !_buffer.isImmutable())
+                _buffer.compact();
+            return _buffer.space()==0;
+        }
+
+        return _content!=null && _content.length()>0;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isWritten()
+    {
+        return _contentWritten>0;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isAllContentWritten()
+    {
+        return _contentLength>=0 && _contentWritten>=_contentLength;
+    }
+
+    /* ------------------------------------------------------------ */
+    public abstract void completeHeader(HttpFields fields, boolean allContentAdded) throws IOException;
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Complete the message.
+     *
+     * @throws IOException
+     */
+    public void complete() throws IOException
+    {
+        if (_state == STATE_HEADER)
+        {
+            throw new IllegalStateException("State==HEADER");
+        }
+
+        if (_contentLength >= 0 && _contentLength != _contentWritten && !_head)
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug("ContentLength written=="+_contentWritten+" != contentLength=="+_contentLength);
+            _persistent = false;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public abstract int flushBuffer() throws IOException;
+
+
+    /* ------------------------------------------------------------ */
+    public void flush(long maxIdleTime) throws IOException
+    {
+        // block until everything is flushed
+        long now=System.currentTimeMillis();
+        long end=now+maxIdleTime;
+        Buffer content = _content;
+        Buffer buffer = _buffer;
+        if (content!=null && content.length()>0 || buffer!=null && buffer.length()>0 || isBufferFull())
+        {
+            flushBuffer();
+
+            while (now<end && (content!=null && content.length()>0 ||buffer!=null && buffer.length()>0) && _endp.isOpen()&& !_endp.isOutputShutdown())
+            {
+                blockForOutput(end-now);
+                now=System.currentTimeMillis();
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Utility method to send an error response. If the builder is not committed, this call is
+     * equivalent to a setResponse, addContent and complete call.
+     *
+     * @param code The error code
+     * @param reason The error reason
+     * @param content Contents of the error page
+     * @param close True if the connection should be closed
+     * @throws IOException if there is a problem flushing the response
+     */
+    public void sendError(int code, String reason, String content, boolean close) throws IOException
+    {
+        if (close)
+            _persistent=false;
+        if (isCommitted())
+        {
+            LOG.debug("sendError on committed: {} {}",code,reason);
+        }
+        else
+        {
+            LOG.debug("sendError: {} {}",code,reason);
+            setResponse(code, reason);
+            if (content != null)
+            {
+                completeHeader(null, false);
+                addContent(new View(new ByteArrayBuffer(content)), Generator.LAST);
+            }
+            else if (code>=400)
+            {
+                completeHeader(null, false);
+                addContent(new View(new ByteArrayBuffer("Error: "+(reason==null?(""+code):reason))), Generator.LAST);
+            }
+            else
+            {
+                completeHeader(null, true);
+            }
+            complete();
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the contentWritten.
+     */
+    public long getContentWritten()
+    {
+        return _contentWritten;
+    }
+
+
+
+    /* ------------------------------------------------------------ */
+    public void  blockForOutput(long maxIdleTime) throws IOException
+    {
+        if (_endp.isBlocking())
+        {
+            try
+            {
+                flushBuffer();
+            }
+            catch(IOException e)
+            {
+                _endp.close();
+                throw e;
+            }
+        }
+        else
+        {
+            if (!_endp.blockWritable(maxIdleTime))
+            {
+                _endp.close();
+                throw new EofException("timeout");
+            }
+
+            flushBuffer();
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/http/EncodedHttpURI.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,183 @@
+//
+//  ========================================================================
+//  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.http;
+
+import java.io.UnsupportedEncodingException;
+
+import org.eclipse.jetty.util.MultiMap;
+import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.TypeUtil;
+import org.eclipse.jetty.util.URIUtil;
+import org.eclipse.jetty.util.UrlEncoded;
+import org.eclipse.jetty.util.Utf8StringBuffer;
+
+public class EncodedHttpURI extends HttpURI
+{
+    private final String _encoding;
+    
+    public EncodedHttpURI(String encoding)
+    {
+        super();
+        _encoding = encoding;
+    }
+    
+    
+    @Override
+    public String getScheme()
+    {
+        if (_scheme==_authority)
+            return null;
+        int l=_authority-_scheme;
+        if (l==5 && 
+            _raw[_scheme]=='h' && 
+            _raw[_scheme+1]=='t' && 
+            _raw[_scheme+2]=='t' && 
+            _raw[_scheme+3]=='p' )
+            return HttpSchemes.HTTP;
+        if (l==6 && 
+            _raw[_scheme]=='h' && 
+            _raw[_scheme+1]=='t' && 
+            _raw[_scheme+2]=='t' && 
+            _raw[_scheme+3]=='p' && 
+            _raw[_scheme+4]=='s' )
+            return HttpSchemes.HTTPS;
+        
+        return StringUtil.toString(_raw,_scheme,_authority-_scheme-1,_encoding);
+    }
+    
+    @Override
+    public String getAuthority()
+    {
+        if (_authority==_path)
+            return null;
+        return StringUtil.toString(_raw,_authority,_path-_authority,_encoding);
+    }
+    
+    @Override
+    public String getHost()
+    {
+        if (_host==_port)
+            return null;
+        return StringUtil.toString(_raw,_host,_port-_host,_encoding);
+    }
+    
+    @Override
+    public int getPort()
+    {
+        if (_port==_path)
+            return -1;
+        return TypeUtil.parseInt(_raw, _port+1, _path-_port-1,10);
+    }
+    
+    @Override
+    public String getPath()
+    {
+        if (_path==_param)
+            return null;
+        return StringUtil.toString(_raw,_path,_param-_path,_encoding);
+    }
+    
+    @Override
+    public String getDecodedPath()
+    {
+        if (_path==_param)
+            return null;
+        return URIUtil.decodePath(_raw,_path,_param-_path);
+    }
+    
+    @Override
+    public String getPathAndParam()
+    {
+        if (_path==_query)
+            return null;
+        return StringUtil.toString(_raw,_path,_query-_path,_encoding);
+    }
+    
+    @Override
+    public String getCompletePath()
+    {
+        if (_path==_end)
+            return null;
+        return StringUtil.toString(_raw,_path,_end-_path,_encoding);
+    }
+    
+    @Override
+    public String getParam()
+    {
+        if (_param==_query)
+            return null;
+        return StringUtil.toString(_raw,_param+1,_query-_param-1,_encoding);
+    }
+    
+    @Override
+    public String getQuery()
+    {
+        if (_query==_fragment)
+            return null;
+        return StringUtil.toString(_raw,_query+1,_fragment-_query-1,_encoding);
+    }
+    
+    @Override
+    public boolean hasQuery()
+    {
+        return (_fragment>_query);
+    }
+    
+    @Override
+    public String getFragment()
+    {
+        if (_fragment==_end)
+            return null;
+        return StringUtil.toString(_raw,_fragment+1,_end-_fragment-1,_encoding);
+    }
+
+    @Override
+    public void decodeQueryTo(MultiMap parameters) 
+    {
+        if (_query==_fragment)
+            return;
+        UrlEncoded.decodeTo(StringUtil.toString(_raw,_query+1,_fragment-_query-1,_encoding),parameters,_encoding);
+    }
+
+    @Override
+    public void decodeQueryTo(MultiMap parameters, String encoding) 
+        throws UnsupportedEncodingException
+    {
+        if (_query==_fragment)
+            return;
+       
+        if (encoding==null)
+            encoding=_encoding;
+        UrlEncoded.decodeTo(StringUtil.toString(_raw,_query+1,_fragment-_query-1,encoding),parameters,encoding);
+    }
+    
+    @Override
+    public String toString()
+    {
+        if (_rawString==null)
+            _rawString= StringUtil.toString(_raw,_scheme,_end-_scheme,_encoding);
+        return _rawString;
+    }
+    
+    public void writeTo(Utf8StringBuffer buf)
+    {
+        buf.getStringBuffer().append(toString());
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/http/Generator.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,96 @@
+//
+//  ========================================================================
+//  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.http;
+
+import java.io.IOException;
+
+import org.eclipse.jetty.io.Buffer;
+
+public interface Generator
+{
+    public static final boolean LAST=true;
+    public static final boolean MORE=false;
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Add content.
+     * 
+     * @param content
+     * @param last
+     * @throws IllegalArgumentException if <code>content</code> is {@link Buffer#isImmutable immutable}.
+     * @throws IllegalStateException If the request is not expecting any more content,
+     *   or if the buffers are full and cannot be flushed.
+     * @throws IOException if there is a problem flushing the buffers.
+     */
+    void addContent(Buffer content, boolean last) throws IOException;
+
+    void complete() throws IOException;
+
+    void completeHeader(HttpFields responseFields, boolean last) throws IOException;
+
+    int flushBuffer() throws IOException;
+
+    int getContentBufferSize();
+
+    long getContentWritten();
+
+    boolean isWritten();
+    
+    boolean isAllContentWritten();
+
+    void increaseContentBufferSize(int size);
+    
+    boolean isBufferFull();
+
+    boolean isCommitted();
+
+    boolean isComplete();
+
+    boolean isPersistent();
+
+    void reset();
+
+    void resetBuffer();
+    
+    void returnBuffers();
+
+    void sendError(int code, String reason, String content, boolean close) throws IOException;
+    
+    void setHead(boolean head);
+
+    void setRequest(String method, String uri);
+
+    void setResponse(int status, String reason);
+
+
+    void setSendServerVersion(boolean sendServerVersion);
+ 
+    void setVersion(int version);
+
+    boolean isIdle();
+
+    void setContentLength(long length);
+    
+    void setPersistent(boolean persistent);
+
+    void setDate(Buffer timeStampBuffer);
+    
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/http/HttpBuffers.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,108 @@
+//
+//  ========================================================================
+//  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.http;
+
+import org.eclipse.jetty.io.Buffers;
+import org.eclipse.jetty.io.BuffersFactory;
+import org.eclipse.jetty.util.component.AbstractLifeCycle;
+
+/* ------------------------------------------------------------ */
+/** Abstract Buffer pool.
+ */
+public interface HttpBuffers
+{
+    /**
+     * @return the requestBufferSize
+     */
+    public int getRequestBufferSize();
+    
+    /**
+     * @param requestBufferSize the requestBufferSize to set
+     */
+    public void setRequestBufferSize(int requestBufferSize);
+
+    /**
+     * @return the requestHeaderSize
+     */
+    public int getRequestHeaderSize();
+
+    /**
+     * @param requestHeaderSize the requestHeaderSize to set
+     */
+    public void setRequestHeaderSize(int requestHeaderSize);
+
+    /**
+     * @return the responseBufferSize
+     */
+    public int getResponseBufferSize();
+
+    /**
+     * @param responseBufferSize the responseBufferSize to set
+     */
+    public void setResponseBufferSize(int responseBufferSize);
+
+    /**
+     * @return the responseHeaderSize
+     */
+    public int getResponseHeaderSize();
+
+    /**
+     * @param responseHeaderSize the responseHeaderSize to set
+     */
+    public void setResponseHeaderSize(int responseHeaderSize);
+
+    /**
+     * @return the requestBufferType
+     */
+    public Buffers.Type getRequestBufferType();
+
+    /**
+     * @return the requestHeaderType
+     */
+    public Buffers.Type getRequestHeaderType();
+
+    /**
+     * @return the responseBufferType
+     */
+    public Buffers.Type getResponseBufferType();
+
+    /**
+     * @return the responseHeaderType
+     */
+    public Buffers.Type getResponseHeaderType();
+
+    /**
+     * @param requestBuffers the requestBuffers to set
+     */
+    public void setRequestBuffers(Buffers requestBuffers);
+
+    /**
+     * @param responseBuffers the responseBuffers to set
+     */
+    public void setResponseBuffers(Buffers responseBuffers);
+
+    public Buffers getRequestBuffers();
+
+    public Buffers getResponseBuffers();
+
+    public void setMaxBuffers(int maxBuffers);
+
+    public int getMaxBuffers();
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/http/HttpBuffersImpl.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,238 @@
+//
+//  ========================================================================
+//  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.http;
+
+import org.eclipse.jetty.io.Buffers;
+import org.eclipse.jetty.io.BuffersFactory;
+import org.eclipse.jetty.util.component.AbstractLifeCycle;
+
+/* ------------------------------------------------------------ */
+/** Abstract Buffer pool.
+ * simple unbounded pool of buffers for header, request and response sizes.
+ *
+ */
+public class HttpBuffersImpl extends AbstractLifeCycle implements HttpBuffers
+{
+    private int _requestBufferSize=16*1024;
+    private int _requestHeaderSize=6*1024;
+    private int _responseBufferSize=32*1024;
+    private int _responseHeaderSize=6*1024;
+    private int _maxBuffers=1024;
+    
+    private Buffers.Type _requestBufferType=Buffers.Type.BYTE_ARRAY;
+    private Buffers.Type _requestHeaderType=Buffers.Type.BYTE_ARRAY;
+    private Buffers.Type _responseBufferType=Buffers.Type.BYTE_ARRAY;
+    private Buffers.Type _responseHeaderType=Buffers.Type.BYTE_ARRAY;
+    
+    private Buffers _requestBuffers;
+    private Buffers _responseBuffers;
+    
+    
+    public HttpBuffersImpl()
+    {
+        super();
+    }
+    
+    /**
+     * @return the requestBufferSize
+     */
+    public int getRequestBufferSize()
+    {
+        return _requestBufferSize;
+    }
+    
+    /**
+     * @param requestBufferSize the requestBufferSize to set
+     */
+    public void setRequestBufferSize(int requestBufferSize)
+    {
+        _requestBufferSize = requestBufferSize;
+    }
+
+    /**
+     * @return the requestHeaderSize
+     */
+    public int getRequestHeaderSize()
+    {
+        return _requestHeaderSize;
+    }
+
+    /**
+     * @param requestHeaderSize the requestHeaderSize to set
+     */
+    public void setRequestHeaderSize(int requestHeaderSize)
+    {
+        _requestHeaderSize = requestHeaderSize;
+    }
+
+    /**
+     * @return the responseBufferSize
+     */
+    public int getResponseBufferSize()
+    {
+        return _responseBufferSize;
+    }
+
+    /**
+     * @param responseBufferSize the responseBufferSize to set
+     */
+    public void setResponseBufferSize(int responseBufferSize)
+    {
+        _responseBufferSize = responseBufferSize;
+    }
+
+    /**
+     * @return the responseHeaderSize
+     */
+    public int getResponseHeaderSize()
+    {
+        return _responseHeaderSize;
+    }
+
+    /**
+     * @param responseHeaderSize the responseHeaderSize to set
+     */
+    public void setResponseHeaderSize(int responseHeaderSize)
+    {
+        _responseHeaderSize = responseHeaderSize;
+    }
+
+    /**
+     * @return the requestBufferType
+     */
+    public Buffers.Type getRequestBufferType()
+    {
+        return _requestBufferType;
+    }
+
+    /**
+     * @param requestBufferType the requestBufferType to set
+     */
+    public void setRequestBufferType(Buffers.Type requestBufferType)
+    {
+        _requestBufferType = requestBufferType;
+    }
+
+    /**
+     * @return the requestHeaderType
+     */
+    public Buffers.Type getRequestHeaderType()
+    {
+        return _requestHeaderType;
+    }
+
+    /**
+     * @param requestHeaderType the requestHeaderType to set
+     */
+    public void setRequestHeaderType(Buffers.Type requestHeaderType)
+    {
+        _requestHeaderType = requestHeaderType;
+    }
+
+    /**
+     * @return the responseBufferType
+     */
+    public Buffers.Type getResponseBufferType()
+    {
+        return _responseBufferType;
+    }
+
+    /**
+     * @param responseBufferType the responseBufferType to set
+     */
+    public void setResponseBufferType(Buffers.Type responseBufferType)
+    {
+        _responseBufferType = responseBufferType;
+    }
+
+    /**
+     * @return the responseHeaderType
+     */
+    public Buffers.Type getResponseHeaderType()
+    {
+        return _responseHeaderType;
+    }
+
+    /**
+     * @param responseHeaderType the responseHeaderType to set
+     */
+    public void setResponseHeaderType(Buffers.Type responseHeaderType)
+    {
+        _responseHeaderType = responseHeaderType;
+    }
+
+    /**
+     * @param requestBuffers the requestBuffers to set
+     */
+    public void setRequestBuffers(Buffers requestBuffers)
+    {
+        _requestBuffers = requestBuffers;
+    }
+
+    /**
+     * @param responseBuffers the responseBuffers to set
+     */
+    public void setResponseBuffers(Buffers responseBuffers)
+    {
+        _responseBuffers = responseBuffers;
+    }
+
+    @Override
+    protected void doStart()
+        throws Exception
+    {
+        _requestBuffers=BuffersFactory.newBuffers(_requestHeaderType,_requestHeaderSize,_requestBufferType,_requestBufferSize,_requestBufferType,getMaxBuffers());
+        _responseBuffers=BuffersFactory.newBuffers(_responseHeaderType,_responseHeaderSize,_responseBufferType,_responseBufferSize,_responseBufferType,getMaxBuffers());
+        super.doStart();
+    }
+    
+    @Override
+    protected void doStop()
+        throws Exception
+    {
+        _requestBuffers=null;
+        _responseBuffers=null;
+    }
+
+    public Buffers getRequestBuffers()
+    {
+        return _requestBuffers;
+    }
+    
+
+    public Buffers getResponseBuffers()
+    {
+        return _responseBuffers;
+    }
+
+    public void setMaxBuffers(int maxBuffers)
+    {
+        _maxBuffers = maxBuffers;
+    }
+
+    public int getMaxBuffers()
+    {
+        return _maxBuffers;
+    }
+    
+    public String toString()
+    {
+        return _requestBuffers+"/"+_responseBuffers;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/http/HttpContent.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,167 @@
+//
+//  ========================================================================
+//  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.http;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.eclipse.jetty.io.Buffer;
+import org.eclipse.jetty.io.ByteArrayBuffer;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.resource.Resource;
+
+/* ------------------------------------------------------------ */
+/** HttpContent.
+ * 
+ *
+ */
+public interface HttpContent
+{
+    Buffer getContentType();
+    Buffer getLastModified();
+    Buffer getIndirectBuffer();
+    Buffer getDirectBuffer();
+    Buffer getETag();
+    Resource getResource();
+    long getContentLength();
+    InputStream getInputStream() throws IOException;
+    void release();
+    
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    public class ResourceAsHttpContent implements HttpContent
+    {
+        private static final Logger LOG = Log.getLogger(ResourceAsHttpContent.class);
+        
+        final Resource _resource;
+        final Buffer _mimeType;
+        final int _maxBuffer;
+        final Buffer _etag;
+
+        /* ------------------------------------------------------------ */
+        public ResourceAsHttpContent(final Resource resource, final Buffer mimeType)
+        {
+            this(resource,mimeType,-1,false);
+        }
+
+        /* ------------------------------------------------------------ */
+        public ResourceAsHttpContent(final Resource resource, final Buffer mimeType, int maxBuffer)
+        {
+            this(resource,mimeType,maxBuffer,false);
+        }
+
+        /* ------------------------------------------------------------ */
+        public ResourceAsHttpContent(final Resource resource, final Buffer mimeType, boolean etag)
+        {
+            this(resource,mimeType,-1,etag);
+        }
+
+        /* ------------------------------------------------------------ */
+        public ResourceAsHttpContent(final Resource resource, final Buffer mimeType, int maxBuffer, boolean etag)
+        {
+            _resource=resource;
+            _mimeType=mimeType;
+            _maxBuffer=maxBuffer;
+            _etag=etag?new ByteArrayBuffer(resource.getWeakETag()):null;
+        }
+
+        /* ------------------------------------------------------------ */
+        public Buffer getContentType()
+        {
+            return _mimeType;
+        }
+
+        /* ------------------------------------------------------------ */
+        public Buffer getLastModified()
+        {
+            return null;
+        }
+
+        /* ------------------------------------------------------------ */
+        public Buffer getDirectBuffer()
+        {
+            return null;
+        }
+        
+        /* ------------------------------------------------------------ */
+        public Buffer getETag()
+        {
+            return _etag;
+        }
+
+        /* ------------------------------------------------------------ */
+        public Buffer getIndirectBuffer()
+        {
+            InputStream inputStream = null;
+            try
+            {
+                if (_resource.length() <= 0 || _maxBuffer < _resource.length())
+                    return null;
+                ByteArrayBuffer buffer = new ByteArrayBuffer((int)_resource.length());
+                inputStream = _resource.getInputStream();
+                buffer.readFrom(inputStream,(int)_resource.length());
+                return buffer;
+            }
+            catch (IOException e)
+            {
+                throw new RuntimeException(e);
+            }
+            finally
+            {
+                if (inputStream != null)
+                {
+                    try
+                    {
+                        inputStream.close();
+                    }
+                    catch (IOException e)
+                    {
+                        LOG.warn("Couldn't close inputStream. Possible file handle leak",e);
+                    }
+                }
+            }
+        }
+
+        /* ------------------------------------------------------------ */
+        public long getContentLength()
+        {
+            return _resource.length();
+        }
+
+        /* ------------------------------------------------------------ */
+        public InputStream getInputStream() throws IOException
+        {
+            return _resource.getInputStream();
+        }
+
+        /* ------------------------------------------------------------ */
+        public Resource getResource()
+        {
+            return _resource;
+        }
+
+        /* ------------------------------------------------------------ */
+        public void release()
+        {
+            _resource.release();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/http/HttpCookie.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,191 @@
+//
+//  ========================================================================
+//  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.http;
+
+public class HttpCookie
+{
+    private final String _name;        
+    private final String _value;     
+    private final String _comment;                               
+    private final String _domain;    
+    private final int _maxAge;  
+    private final String _path;       
+    private final boolean _secure;   
+    private final int _version;   
+    private final boolean _httpOnly;
+
+    /* ------------------------------------------------------------ */
+    public HttpCookie(String name, String value)
+    {
+        super();
+        _name = name;
+        _value = value;
+        _comment = null;
+        _domain = null;
+        _httpOnly = false;
+        _maxAge = -1;
+        _path = null;
+        _secure = false;
+        _version = 0;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public HttpCookie(String name, String value, String domain, String path)
+    {
+        super();
+        _name = name;
+        _value = value;
+        _comment = null;
+        _domain = domain;
+        _httpOnly = false;
+        _maxAge = -1;
+        _path = path;
+        _secure = false;
+        _version = 0;
+        
+    }
+    
+    /* ------------------------------------------------------------ */
+    public HttpCookie(String name, String value, int maxAge)
+    {
+        super();
+        _name = name;
+        _value = value;
+        _comment = null;
+        _domain = null;
+        _httpOnly = false;
+        _maxAge = maxAge;
+        _path = null;
+        _secure = false;
+        _version = 0;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public HttpCookie(String name, String value, String domain, String path, int maxAge, boolean httpOnly, boolean secure)
+    {
+        super();
+        _comment = null;
+        _domain = domain;
+        _httpOnly = httpOnly;
+        _maxAge = maxAge;
+        _name = name;
+        _path = path;
+        _secure = secure;
+        _value = value;
+        _version = 0;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public HttpCookie(String name, String value, String domain, String path, int maxAge, boolean httpOnly, boolean secure, String comment, int version)
+    {
+        super();
+        _comment = comment;
+        _domain = domain;
+        _httpOnly = httpOnly;
+        _maxAge = maxAge;
+        _name = name;
+        _path = path;
+        _secure = secure;
+        _value = value;
+        _version = version;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Get the name.
+     * @return the name
+     */
+    public String getName()
+    {
+        return _name;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Get the value.
+     * @return the value
+     */
+    public String getValue()
+    {
+        return _value;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Get the comment.
+     * @return the comment
+     */
+    public String getComment()
+    {
+        return _comment;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Get the domain.
+     * @return the domain
+     */
+    public String getDomain()
+    {
+        return _domain;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Get the maxAge.
+     * @return the maxAge
+     */
+    public int getMaxAge()
+    {
+        return _maxAge;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Get the path.
+     * @return the path
+     */
+    public String getPath()
+    {
+        return _path;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Get the secure.
+     * @return the secure
+     */
+    public boolean isSecure()
+    {
+        return _secure;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Get the version.
+     * @return the version
+     */
+    public int getVersion()
+    {
+        return _version;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Get the isHttpOnly.
+     * @return the isHttpOnly
+     */
+    public boolean isHttpOnly()
+    {
+        return _httpOnly;
+    }
+    
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/http/HttpException.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,94 @@
+//
+//  ========================================================================
+//  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.http;
+
+import java.io.IOException;
+
+public class HttpException extends IOException
+{
+    int _status;
+    String _reason;
+
+    /* ------------------------------------------------------------ */
+    public HttpException(int status)
+    {
+        _status=status;
+        _reason=null;
+    }
+
+    /* ------------------------------------------------------------ */
+    public HttpException(int status,String reason)
+    {
+        _status=status;
+        _reason=reason;
+    }
+
+    /* ------------------------------------------------------------ */
+    public HttpException(int status,String reason, Throwable rootCause)
+    {
+        _status=status;
+        _reason=reason;
+        initCause(rootCause);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the reason.
+     */
+    public String getReason()
+    {
+        return _reason;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param reason The reason to set.
+     */
+    public void setReason(String reason)
+    {
+        _reason = reason;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the status.
+     */
+    public int getStatus()
+    {
+        return _status;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param status The status to set.
+     */
+    public void setStatus(int status)
+    {
+        _status = status;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public String toString()
+    {
+        return ("HttpException("+_status+","+_reason+","+super.getCause()+")");
+    }
+    
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/http/HttpFields.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,1406 @@
+//
+//  ========================================================================
+//  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.http;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.StringTokenizer;
+import java.util.TimeZone;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.eclipse.jetty.io.Buffer;
+import org.eclipse.jetty.io.BufferCache;
+import org.eclipse.jetty.io.BufferCache.CachedBuffer;
+import org.eclipse.jetty.io.BufferDateCache;
+import org.eclipse.jetty.io.BufferUtil;
+import org.eclipse.jetty.io.ByteArrayBuffer;
+import org.eclipse.jetty.util.LazyList;
+import org.eclipse.jetty.util.QuotedStringTokenizer;
+import org.eclipse.jetty.util.StringMap;
+import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/* ------------------------------------------------------------ */
+/**
+ * HTTP Fields. A collection of HTTP header and or Trailer fields. 
+ * 
+ * <p>This class is not synchronized as it is expected that modifications will only be performed by a
+ * single thread.
+ * 
+ * 
+ */
+public class HttpFields
+{
+    private static final Logger LOG = Log.getLogger(HttpFields.class);
+    
+    /* ------------------------------------------------------------ */
+    public static final String __COOKIE_DELIM="\"\\\n\r\t\f\b%+ ;=";
+    public static final TimeZone __GMT = TimeZone.getTimeZone("GMT");
+    public static final BufferDateCache __dateCache = new BufferDateCache("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US);
+
+    /* -------------------------------------------------------------- */
+    static
+    {
+        __GMT.setID("GMT");
+        __dateCache.setTimeZone(__GMT);
+    }
+    
+    /* ------------------------------------------------------------ */
+    public final static String __separators = ", \t";
+
+    /* ------------------------------------------------------------ */
+    private static final String[] DAYS =
+    { "Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
+    private static final String[] MONTHS =
+    { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "Jan"};
+
+    
+    /* ------------------------------------------------------------ */
+    private static class DateGenerator
+    {
+        private final StringBuilder buf = new StringBuilder(32);
+        private final GregorianCalendar gc = new GregorianCalendar(__GMT);
+        
+        /**
+         * Format HTTP date "EEE, dd MMM yyyy HH:mm:ss 'GMT'" 
+         */
+        public String formatDate(long date)
+        {
+            buf.setLength(0);
+            gc.setTimeInMillis(date);
+            
+            int day_of_week = gc.get(Calendar.DAY_OF_WEEK);
+            int day_of_month = gc.get(Calendar.DAY_OF_MONTH);
+            int month = gc.get(Calendar.MONTH);
+            int year = gc.get(Calendar.YEAR);
+            int century = year / 100;
+            year = year % 100;
+            
+            int hours = gc.get(Calendar.HOUR_OF_DAY);
+            int minutes = gc.get(Calendar.MINUTE);
+            int seconds = gc.get(Calendar.SECOND);
+
+            buf.append(DAYS[day_of_week]);
+            buf.append(',');
+            buf.append(' ');
+            StringUtil.append2digits(buf, day_of_month);
+
+            buf.append(' ');
+            buf.append(MONTHS[month]);
+            buf.append(' ');
+            StringUtil.append2digits(buf, century);
+            StringUtil.append2digits(buf, year);
+            
+            buf.append(' ');
+            StringUtil.append2digits(buf, hours);
+            buf.append(':');
+            StringUtil.append2digits(buf, minutes);
+            buf.append(':');
+            StringUtil.append2digits(buf, seconds);
+            buf.append(" GMT");
+            return buf.toString();
+        }
+
+        /* ------------------------------------------------------------ */
+        /**
+         * Format "EEE, dd-MMM-yy HH:mm:ss 'GMT'" for cookies
+         */
+        public void formatCookieDate(StringBuilder buf, long date)
+        {
+            gc.setTimeInMillis(date);
+            
+            int day_of_week = gc.get(Calendar.DAY_OF_WEEK);
+            int day_of_month = gc.get(Calendar.DAY_OF_MONTH);
+            int month = gc.get(Calendar.MONTH);
+            int year = gc.get(Calendar.YEAR);
+            year = year % 10000;
+
+            int epoch = (int) ((date / 1000) % (60 * 60 * 24));
+            int seconds = epoch % 60;
+            epoch = epoch / 60;
+            int minutes = epoch % 60;
+            int hours = epoch / 60;
+
+            buf.append(DAYS[day_of_week]);
+            buf.append(',');
+            buf.append(' ');
+            StringUtil.append2digits(buf, day_of_month);
+
+            buf.append('-');
+            buf.append(MONTHS[month]);
+            buf.append('-');
+            StringUtil.append2digits(buf, year/100);
+            StringUtil.append2digits(buf, year%100);
+            
+            buf.append(' ');
+            StringUtil.append2digits(buf, hours);
+            buf.append(':');
+            StringUtil.append2digits(buf, minutes);
+            buf.append(':');
+            StringUtil.append2digits(buf, seconds);
+            buf.append(" GMT");
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    private static final ThreadLocal<DateGenerator> __dateGenerator =new ThreadLocal<DateGenerator>()
+    {
+        @Override
+        protected DateGenerator initialValue()
+        {
+            return new DateGenerator();
+        }
+    };
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Format HTTP date "EEE, dd MMM yyyy HH:mm:ss 'GMT'" 
+     */
+    public static String formatDate(long date)
+    {
+        return __dateGenerator.get().formatDate(date);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Format "EEE, dd-MMM-yyyy HH:mm:ss 'GMT'" for cookies
+     */
+    public static void formatCookieDate(StringBuilder buf, long date)
+    {
+        __dateGenerator.get().formatCookieDate(buf,date);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Format "EEE, dd-MMM-yyyy HH:mm:ss 'GMT'" for cookies
+     */
+    public static String formatCookieDate(long date)
+    {
+        StringBuilder buf = new StringBuilder(28);
+        formatCookieDate(buf, date);
+        return buf.toString();
+    }
+
+    /* ------------------------------------------------------------ */
+    private final static String __dateReceiveFmt[] =
+    {   
+        "EEE, dd MMM yyyy HH:mm:ss zzz", 
+        "EEE, dd-MMM-yy HH:mm:ss",
+        "EEE MMM dd HH:mm:ss yyyy",
+
+        "EEE, dd MMM yyyy HH:mm:ss", "EEE dd MMM yyyy HH:mm:ss zzz", 
+        "EEE dd MMM yyyy HH:mm:ss", "EEE MMM dd yyyy HH:mm:ss zzz", "EEE MMM dd yyyy HH:mm:ss", 
+        "EEE MMM-dd-yyyy HH:mm:ss zzz", "EEE MMM-dd-yyyy HH:mm:ss", "dd MMM yyyy HH:mm:ss zzz", 
+        "dd MMM yyyy HH:mm:ss", "dd-MMM-yy HH:mm:ss zzz", "dd-MMM-yy HH:mm:ss", "MMM dd HH:mm:ss yyyy zzz", 
+        "MMM dd HH:mm:ss yyyy", "EEE MMM dd HH:mm:ss yyyy zzz",  
+        "EEE, MMM dd HH:mm:ss yyyy zzz", "EEE, MMM dd HH:mm:ss yyyy", "EEE, dd-MMM-yy HH:mm:ss zzz", 
+        "EEE dd-MMM-yy HH:mm:ss zzz", "EEE dd-MMM-yy HH:mm:ss",
+    };
+
+    /* ------------------------------------------------------------ */
+    private static class DateParser
+    {
+        final SimpleDateFormat _dateReceive[]= new SimpleDateFormat[__dateReceiveFmt.length];
+ 
+        long parse(final String dateVal)
+        {
+            for (int i = 0; i < _dateReceive.length; i++)
+            {
+                if (_dateReceive[i] == null)
+                {
+                    _dateReceive[i] = new SimpleDateFormat(__dateReceiveFmt[i], Locale.US);
+                    _dateReceive[i].setTimeZone(__GMT);
+                }
+
+                try
+                {
+                    Date date = (Date) _dateReceive[i].parseObject(dateVal);
+                    return date.getTime();
+                }
+                catch (java.lang.Exception e)
+                {
+                    // LOG.ignore(e);
+                }
+            }
+            
+            if (dateVal.endsWith(" GMT"))
+            {
+                final String val = dateVal.substring(0, dateVal.length() - 4);
+
+                for (int i = 0; i < _dateReceive.length; i++)
+                {
+                    try
+                    {
+                        Date date = (Date) _dateReceive[i].parseObject(val);
+                        return date.getTime();
+                    }
+                    catch (java.lang.Exception e)
+                    {
+                        // LOG.ignore(e);
+                    }
+                }
+            }    
+            return -1;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public static long parseDate(String date)
+    {
+        return __dateParser.get().parse(date);
+    }
+
+    /* ------------------------------------------------------------ */
+    private static final ThreadLocal<DateParser> __dateParser =new ThreadLocal<DateParser>()
+    {
+        @Override
+        protected DateParser initialValue()
+        {
+            return new DateParser();
+        }
+    };
+
+    /* -------------------------------------------------------------- */
+    public final static String __01Jan1970=formatDate(0);
+    public final static Buffer __01Jan1970_BUFFER=new ByteArrayBuffer(__01Jan1970);
+    public final static String __01Jan1970_COOKIE = formatCookieDate(0).trim();
+
+    /* -------------------------------------------------------------- */
+    private final ArrayList<Field> _fields = new ArrayList<Field>(20);
+    private final HashMap<Buffer,Field> _names = new HashMap<Buffer,Field>(32);
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Constructor.
+     */
+    public HttpFields()
+    {
+    }
+
+    // TODO externalize this cache so it can be configurable
+    private static ConcurrentMap<String, Buffer> __cache = new ConcurrentHashMap<String, Buffer>();
+    private static int __cacheSize = Integer.getInteger("org.eclipse.jetty.http.HttpFields.CACHE",2000);
+    
+    /* -------------------------------------------------------------- */
+    private Buffer convertValue(String value)
+    {
+        Buffer buffer = __cache.get(value);
+        if (buffer!=null)
+            return buffer;
+        
+        try
+        {   
+            buffer = new ByteArrayBuffer(value,StringUtil.__ISO_8859_1);
+            
+            if (__cacheSize>0)
+            {
+                if (__cache.size()>__cacheSize)
+                    __cache.clear();
+                Buffer b=__cache.putIfAbsent(value,buffer);
+                if (b!=null)
+                    buffer=b;
+            }
+            
+            return buffer;
+        }
+        catch (UnsupportedEncodingException e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+    
+    /* -------------------------------------------------------------- */
+    /**
+     * Get Collection of header names. 
+     */
+    public Collection<String> getFieldNamesCollection()
+    {
+        final List<String> list = new ArrayList<String>(_fields.size());
+
+	for (Field f : _fields)
+	{
+	    if (f!=null)
+	        list.add(BufferUtil.to8859_1_String(f._name));
+	}
+	return list;
+    }
+    
+    /* -------------------------------------------------------------- */
+    /**
+     * Get enumeration of header _names. Returns an enumeration of strings representing the header
+     * _names for this request.
+     */
+    public Enumeration<String> getFieldNames()
+    {
+        final Enumeration<?> buffers = Collections.enumeration(_names.keySet());
+        return new Enumeration<String>()
+        {
+            public String nextElement()
+            {
+                return buffers.nextElement().toString();
+            }
+            
+            public boolean hasMoreElements()
+            {
+                return buffers.hasMoreElements();
+            }
+        }; 
+    }
+    
+    /* ------------------------------------------------------------ */
+    public int size()
+    {
+        return _fields.size();
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Get a Field by index.
+     * @return A Field value or null if the Field value has not been set
+     * 
+     */
+    public Field getField(int i)
+    {
+        return _fields.get(i);
+    }
+
+    /* ------------------------------------------------------------ */
+    private Field getField(String name)
+    {
+        return _names.get(HttpHeaders.CACHE.lookup(name));
+    }
+
+    /* ------------------------------------------------------------ */
+    private Field getField(Buffer name)
+    {
+        return _names.get(HttpHeaders.CACHE.lookup(name));
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean containsKey(Buffer name)
+    {
+        return _names.containsKey(HttpHeaders.CACHE.lookup(name));
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean containsKey(String name)
+    {
+        return _names.containsKey(HttpHeaders.CACHE.lookup(name));
+    }
+
+    /* -------------------------------------------------------------- */
+    /**
+     * @return the value of a field, or null if not found. For multiple fields of the same name,
+     *         only the first is returned.
+     * @param name the case-insensitive field name
+     */
+    public String getStringField(String name)
+    {
+        Field field = getField(name);
+        return field==null?null:field.getValue();
+    }
+
+    /* -------------------------------------------------------------- */
+    /**
+     * @return the value of a field, or null if not found. For multiple fields of the same name,
+     *         only the first is returned.
+     * @param name the case-insensitive field name
+     */
+    public String getStringField(Buffer name)
+    {
+        Field field = getField(name);
+        return field==null?null:field.getValue();
+    }
+
+    /* -------------------------------------------------------------- */
+    /**
+     * @return the value of a field, or null if not found. For multiple fields of the same name,
+     *         only the first is returned.
+     * @param name the case-insensitive field name
+     */
+    public Buffer get(Buffer name)
+    {
+        Field field = getField(name);
+        return field==null?null:field._value;
+    }
+
+
+    /* -------------------------------------------------------------- */
+    /**
+     * Get multi headers
+     * 
+     * @return Enumeration of the values, or null if no such header.
+     * @param name the case-insensitive field name
+     */
+    public Collection<String> getValuesCollection(String name)
+    {
+        Field field = getField(name);
+	if (field==null)
+	    return null;
+
+        final List<String> list = new ArrayList<String>();
+
+	while(field!=null)
+	{
+	    list.add(field.getValue());
+	    field=field._next;
+	}
+	return list;
+    }
+
+    /* -------------------------------------------------------------- */
+    /**
+     * Get multi headers
+     * 
+     * @return Enumeration of the values
+     * @param name the case-insensitive field name
+     */
+    public Enumeration<String> getValues(String name)
+    {
+        final Field field = getField(name);
+        if (field == null) 
+        {
+            List<String> empty=Collections.emptyList();
+            return Collections.enumeration(empty);
+        }
+
+        return new Enumeration<String>()
+        {
+            Field f = field;
+
+            public boolean hasMoreElements()
+            {
+                return f != null;
+            }
+
+            public String nextElement() throws NoSuchElementException
+            {
+                if (f == null) throw new NoSuchElementException();
+                Field n = f;
+                f = f._next;
+                return n.getValue();
+            }
+        };
+    }
+
+    /* -------------------------------------------------------------- */
+    /**
+     * Get multi headers
+     * 
+     * @return Enumeration of the value Strings
+     * @param name the case-insensitive field name
+     */
+    public Enumeration<String> getValues(Buffer name)
+    {
+        final Field field = getField(name);
+        if (field == null) 
+        {
+            List<String> empty=Collections.emptyList();
+            return Collections.enumeration(empty);
+        }
+
+        return new Enumeration<String>()
+        {
+            Field f = field;
+
+            public boolean hasMoreElements()
+            {
+                return f != null;
+            }
+
+            public String nextElement() throws NoSuchElementException
+            {
+                if (f == null) throw new NoSuchElementException();
+                Field n = f;
+                f = f._next;
+                return n.getValue();
+            }
+        };
+    }
+
+    /* -------------------------------------------------------------- */
+    /**
+     * Get multi field values with separator. The multiple values can be represented as separate
+     * headers of the same name, or by a single header using the separator(s), or a combination of
+     * both. Separators may be quoted.
+     * 
+     * @param name the case-insensitive field name
+     * @param separators String of separators.
+     * @return Enumeration of the values, or null if no such header.
+     */
+    public Enumeration<String> getValues(String name, final String separators)
+    {
+        final Enumeration<String> e = getValues(name);
+        if (e == null) 
+            return null;
+        return new Enumeration<String>()
+        {
+            QuotedStringTokenizer tok = null;
+
+            public boolean hasMoreElements()
+            {
+                if (tok != null && tok.hasMoreElements()) return true;
+                while (e.hasMoreElements())
+                {
+                    String value = e.nextElement();
+                    tok = new QuotedStringTokenizer(value, separators, false, false);
+                    if (tok.hasMoreElements()) return true;
+                }
+                tok = null;
+                return false;
+            }
+
+            public String nextElement() throws NoSuchElementException
+            {
+                if (!hasMoreElements()) throw new NoSuchElementException();
+                String next = (String) tok.nextElement();
+                if (next != null) next = next.trim();
+                return next;
+            }
+        };
+    }
+
+    
+    /* -------------------------------------------------------------- */
+    /**
+     * Set a field.
+     * 
+     * @param name the name of the field
+     * @param value the value of the field. If null the field is cleared.
+     */
+    public void put(String name, String value)
+    {
+        if (value==null)
+            remove(name);
+        else
+        {
+            Buffer n = HttpHeaders.CACHE.lookup(name);
+            Buffer v = convertValue(value);
+            put(n, v);
+        }
+    }
+
+    /* -------------------------------------------------------------- */
+    /**
+     * Set a field.
+     * 
+     * @param name the name of the field
+     * @param value the value of the field. If null the field is cleared.
+     */
+    public void put(Buffer name, String value)
+    {
+        Buffer n = HttpHeaders.CACHE.lookup(name);
+        Buffer v = convertValue(value);
+        put(n, v);
+    }
+
+    /* -------------------------------------------------------------- */
+    /**
+     * Set a field.
+     * 
+     * @param name the name of the field
+     * @param value the value of the field. If null the field is cleared.
+     */
+    public void put(Buffer name, Buffer value)
+    {
+        remove(name);
+        if (value == null)
+            return;
+
+        if (!(name instanceof BufferCache.CachedBuffer)) 
+            name = HttpHeaders.CACHE.lookup(name);
+        if (!(value instanceof CachedBuffer))
+            value= HttpHeaderValues.CACHE.lookup(value).asImmutableBuffer();
+        
+        // new value;
+        Field field = new Field(name, value);
+        _fields.add(field);
+        _names.put(name, field);
+    }
+
+    /* -------------------------------------------------------------- */
+    /**
+     * Set a field.
+     * 
+     * @param name the name of the field
+     * @param list the List value of the field. If null the field is cleared.
+     */
+    public void put(String name, List<?> list)
+    {
+        if (list == null || list.size() == 0)
+        {
+            remove(name);
+            return;
+        }
+        Buffer n = HttpHeaders.CACHE.lookup(name);
+
+        Object v = list.get(0);
+        if (v != null)
+            put(n, HttpHeaderValues.CACHE.lookup(v.toString()));
+        else
+            remove(n);
+
+        if (list.size() > 1)
+        {
+            java.util.Iterator<?> iter = list.iterator();
+            iter.next();
+            while (iter.hasNext())
+            {
+                v = iter.next();
+                if (v != null) put(n, HttpHeaderValues.CACHE.lookup(v.toString()));
+            }
+        }
+    }
+
+    /* -------------------------------------------------------------- */
+    /**
+     * Add to or set a field. If the field is allowed to have multiple values, add will add multiple
+     * headers of the same name.
+     * 
+     * @param name the name of the field
+     * @param value the value of the field.
+     * @exception IllegalArgumentException If the name is a single valued field and already has a
+     *                value.
+     */
+    public void add(String name, String value) throws IllegalArgumentException
+    {
+        if (value==null)
+            return;
+        Buffer n = HttpHeaders.CACHE.lookup(name);
+        Buffer v = convertValue(value);
+        add(n, v);
+    }
+
+    /* -------------------------------------------------------------- */
+    /**
+     * Add to or set a field. If the field is allowed to have multiple values, add will add multiple
+     * headers of the same name.
+     * 
+     * @param name the name of the field
+     * @param value the value of the field.
+     * @exception IllegalArgumentException If the name is a single valued field and already has a
+     *                value.
+     */
+    public void add(Buffer name, Buffer value) throws IllegalArgumentException
+    {   
+        if (value == null) throw new IllegalArgumentException("null value");
+
+        if (!(name instanceof CachedBuffer))
+            name = HttpHeaders.CACHE.lookup(name);
+        name=name.asImmutableBuffer();
+        
+        if (!(value instanceof CachedBuffer) && HttpHeaderValues.hasKnownValues(HttpHeaders.CACHE.getOrdinal(name)))
+            value= HttpHeaderValues.CACHE.lookup(value);
+        value=value.asImmutableBuffer();
+        
+        Field field = _names.get(name);
+        Field last = null;
+        while (field != null)
+        {
+            last = field;
+            field = field._next;
+        }
+
+        // create the field
+        field = new Field(name, value);
+        _fields.add(field);
+
+        // look for chain to add too
+        if (last != null)
+            last._next = field;
+        else
+            _names.put(name, field);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Remove a field.
+     * 
+     * @param name
+     */
+    public void remove(String name)
+    {
+        remove(HttpHeaders.CACHE.lookup(name));
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Remove a field.
+     * 
+     * @param name
+     */
+    public void remove(Buffer name)
+    {
+        if (!(name instanceof BufferCache.CachedBuffer)) 
+            name = HttpHeaders.CACHE.lookup(name);
+        Field field = _names.remove(name);
+        while (field != null)
+        {
+            _fields.remove(field);
+            field = field._next;
+        }
+    }
+
+    /* -------------------------------------------------------------- */
+    /**
+     * Get a header as an long value. Returns the value of an integer field or -1 if not found. The
+     * case of the field name is ignored.
+     * 
+     * @param name the case-insensitive field name
+     * @exception NumberFormatException If bad long found
+     */
+    public long getLongField(String name) throws NumberFormatException
+    {
+        Field field = getField(name);
+        return field==null?-1L:field.getLongValue();
+    }
+
+    /* -------------------------------------------------------------- */
+    /**
+     * Get a header as an long value. Returns the value of an integer field or -1 if not found. The
+     * case of the field name is ignored.
+     * 
+     * @param name the case-insensitive field name
+     * @exception NumberFormatException If bad long found
+     */
+    public long getLongField(Buffer name) throws NumberFormatException
+    {
+        Field field = getField(name);
+        return field==null?-1L:field.getLongValue();
+    }
+
+    /* -------------------------------------------------------------- */
+    /**
+     * Get a header as a date value. Returns the value of a date field, or -1 if not found. The case
+     * of the field name is ignored.
+     * 
+     * @param name the case-insensitive field name
+     */
+    public long getDateField(String name)
+    {
+        Field field = getField(name);
+        if (field == null) 
+            return -1;
+
+        String val = valueParameters(BufferUtil.to8859_1_String(field._value), null);
+        if (val == null) 
+            return -1;
+
+        final long date = __dateParser.get().parse(val);
+        if (date==-1)
+            throw new IllegalArgumentException("Cannot convert date: " + val);
+        return date;
+    }
+
+    /* -------------------------------------------------------------- */
+    /**
+     * Sets the value of an long field.
+     * 
+     * @param name the field name
+     * @param value the field long value
+     */
+    public void putLongField(Buffer name, long value)
+    {
+        Buffer v = BufferUtil.toBuffer(value);
+        put(name, v);
+    }
+
+    /* -------------------------------------------------------------- */
+    /**
+     * Sets the value of an long field.
+     * 
+     * @param name the field name
+     * @param value the field long value
+     */
+    public void putLongField(String name, long value)
+    {
+        Buffer n = HttpHeaders.CACHE.lookup(name);
+        Buffer v = BufferUtil.toBuffer(value);
+        put(n, v);
+    }
+
+    /* -------------------------------------------------------------- */
+    /**
+     * Sets the value of an long field.
+     * 
+     * @param name the field name
+     * @param value the field long value
+     */
+    public void addLongField(String name, long value)
+    {
+        Buffer n = HttpHeaders.CACHE.lookup(name);
+        Buffer v = BufferUtil.toBuffer(value);
+        add(n, v);
+    }
+
+    /* -------------------------------------------------------------- */
+    /**
+     * Sets the value of an long field.
+     * 
+     * @param name the field name
+     * @param value the field long value
+     */
+    public void addLongField(Buffer name, long value)
+    {
+        Buffer v = BufferUtil.toBuffer(value);
+        add(name, v);
+    }
+
+    /* -------------------------------------------------------------- */
+    /**
+     * Sets the value of a date field.
+     * 
+     * @param name the field name
+     * @param date the field date value
+     */
+    public void putDateField(Buffer name, long date)
+    {
+        String d=formatDate(date);
+        Buffer v = new ByteArrayBuffer(d);
+        put(name, v);
+    }
+
+    /* -------------------------------------------------------------- */
+    /**
+     * Sets the value of a date field.
+     * 
+     * @param name the field name
+     * @param date the field date value
+     */
+    public void putDateField(String name, long date)
+    {
+        Buffer n = HttpHeaders.CACHE.lookup(name);
+        putDateField(n,date);
+    }
+
+    /* -------------------------------------------------------------- */
+    /**
+     * Sets the value of a date field.
+     * 
+     * @param name the field name
+     * @param date the field date value
+     */
+    public void addDateField(String name, long date)
+    {
+        String d=formatDate(date);
+        Buffer n = HttpHeaders.CACHE.lookup(name);
+        Buffer v = new ByteArrayBuffer(d);
+        add(n, v);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Format a set cookie value
+     * 
+     * @param cookie The cookie.
+     */
+    public void addSetCookie(HttpCookie cookie)
+    {
+        addSetCookie(
+                cookie.getName(),
+                cookie.getValue(),
+                cookie.getDomain(),
+                cookie.getPath(),
+                cookie.getMaxAge(),
+                cookie.getComment(),
+                cookie.isSecure(),
+                cookie.isHttpOnly(),
+                cookie.getVersion());
+    }
+
+    /**
+     * Format a set cookie value
+     * 
+     * @param name the name
+     * @param value the value
+     * @param domain the domain
+     * @param path the path
+     * @param maxAge the maximum age
+     * @param comment the comment (only present on versions > 0)
+     * @param isSecure true if secure cookie
+     * @param isHttpOnly true if for http only
+     * @param version version of cookie logic to use (0 == default behavior)
+     */
+    public void addSetCookie(
+            final String name, 
+            final String value, 
+            final String domain,
+            final String path, 
+            final long maxAge,
+            final String comment, 
+            final boolean isSecure,
+            final boolean isHttpOnly, 
+            int version)
+    {
+    	String delim=__COOKIE_DELIM;
+    	
+        // Check arguments
+        if (name == null || name.length() == 0) 
+            throw new IllegalArgumentException("Bad cookie name");
+
+        // Format value and params
+        StringBuilder buf = new StringBuilder(128);
+        String name_value_params;
+        QuotedStringTokenizer.quoteIfNeeded(buf, name, delim);
+        buf.append('=');
+        String start=buf.toString();
+        boolean hasDomain = false;
+        boolean hasPath = false;
+        
+        if (value != null && value.length() > 0)
+            QuotedStringTokenizer.quoteIfNeeded(buf, value, delim);        
+
+        if (comment != null && comment.length() > 0)
+        {
+            buf.append(";Comment=");
+            QuotedStringTokenizer.quoteIfNeeded(buf, comment, delim);
+        }
+
+        if (path != null && path.length() > 0)
+        {
+            hasPath = true;
+            buf.append(";Path=");
+            if (path.trim().startsWith("\""))
+                buf.append(path);
+            else
+                QuotedStringTokenizer.quoteIfNeeded(buf,path,delim);
+        }
+        if (domain != null && domain.length() > 0)
+        {
+            hasDomain = true;
+            buf.append(";Domain=");
+            QuotedStringTokenizer.quoteIfNeeded(buf,domain.toLowerCase(Locale.ENGLISH),delim);
+        }
+
+        if (maxAge >= 0)
+        {
+            // Always add the expires param as some browsers still don't handle max-age
+            buf.append(";Expires=");
+            if (maxAge == 0)
+                buf.append(__01Jan1970_COOKIE);
+            else
+                formatCookieDate(buf, System.currentTimeMillis() + 1000L * maxAge);
+
+            if (version >0)
+            {
+                buf.append(";Max-Age=");
+                buf.append(maxAge);
+            }
+        }
+
+        if (isSecure)
+            buf.append(";Secure");
+        if (isHttpOnly) 
+            buf.append(";HttpOnly");
+
+        name_value_params = buf.toString();
+        
+        // remove existing set-cookie of same name
+        Field field = getField(HttpHeaders.SET_COOKIE);
+        Field last=null;
+        while (field!=null)
+        {
+            String val = (field._value == null ? null : field._value.toString());
+            if (val!=null && val.startsWith(start))
+            {
+                //existing cookie has same name, does it also match domain and path?
+                if (((!hasDomain && !val.contains("Domain")) || (hasDomain && val.contains("Domain="+domain))) &&
+                    ((!hasPath && !val.contains("Path")) || (hasPath && val.contains("Path="+path))))
+                {
+                    _fields.remove(field);
+                    if (last==null)
+                        _names.put(HttpHeaders.SET_COOKIE_BUFFER,field._next);
+                    else
+                        last._next=field._next;
+                    break;
+                }
+            }
+            last=field;
+            field=field._next;
+        }
+
+        add(HttpHeaders.SET_COOKIE_BUFFER, new ByteArrayBuffer(name_value_params));
+        
+        // Expire responses with set-cookie headers so they do not get cached.
+        put(HttpHeaders.EXPIRES_BUFFER, __01Jan1970_BUFFER);
+    }
+
+    /* -------------------------------------------------------------- */
+    public void putTo(Buffer buffer) throws IOException
+    {
+        for (int i = 0; i < _fields.size(); i++)
+        {
+            Field field = _fields.get(i);
+            if (field != null) 
+                field.putTo(buffer);
+        }
+        BufferUtil.putCRLF(buffer);
+    }
+
+    /* -------------------------------------------------------------- */
+    public String toString()
+    {
+        try
+        {
+            StringBuffer buffer = new StringBuffer();
+            for (int i = 0; i < _fields.size(); i++)
+            {
+                Field field = (Field) _fields.get(i);
+                if (field != null)
+                {
+                    String tmp = field.getName();
+                    if (tmp != null) buffer.append(tmp);
+                    buffer.append(": ");
+                    tmp = field.getValue();
+                    if (tmp != null) buffer.append(tmp);
+                    buffer.append("\r\n");
+                }
+            }
+            buffer.append("\r\n");
+            return buffer.toString();
+        }
+        catch (Exception e)
+        {
+            LOG.warn(e);
+            return e.toString();
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Clear the header.
+     */
+    public void clear()
+    {
+        _fields.clear();
+        _names.clear();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Add fields from another HttpFields instance. Single valued fields are replaced, while all
+     * others are added.
+     * 
+     * @param fields
+     */
+    public void add(HttpFields fields)
+    {
+        if (fields == null) return;
+
+        Enumeration e = fields.getFieldNames();
+        while (e.hasMoreElements())
+        {
+            String name = (String) e.nextElement();
+            Enumeration values = fields.getValues(name);
+            while (values.hasMoreElements())
+                add(name, (String) values.nextElement());
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Get field value parameters. Some field values can have parameters. This method separates the
+     * value from the parameters and optionally populates a map with the parameters. For example:
+     * 
+     * <PRE>
+     * 
+     * FieldName : Value ; param1=val1 ; param2=val2
+     * 
+     * </PRE>
+     * 
+     * @param value The Field value, possibly with parameteres.
+     * @param parameters A map to populate with the parameters, or null
+     * @return The value.
+     */
+    public static String valueParameters(String value, Map<String,String> parameters)
+    {
+        if (value == null) return null;
+
+        int i = value.indexOf(';');
+        if (i < 0) return value;
+        if (parameters == null) return value.substring(0, i).trim();
+
+        StringTokenizer tok1 = new QuotedStringTokenizer(value.substring(i), ";", false, true);
+        while (tok1.hasMoreTokens())
+        {
+            String token = tok1.nextToken();
+            StringTokenizer tok2 = new QuotedStringTokenizer(token, "= ");
+            if (tok2.hasMoreTokens())
+            {
+                String paramName = tok2.nextToken();
+                String paramVal = null;
+                if (tok2.hasMoreTokens()) paramVal = tok2.nextToken();
+                parameters.put(paramName, paramVal);
+            }
+        }
+
+        return value.substring(0, i).trim();
+    }
+
+    /* ------------------------------------------------------------ */
+    private static final Float __one = new Float("1.0");
+    private static final Float __zero = new Float("0.0");
+    private static final StringMap __qualities = new StringMap();
+    static
+    {
+        __qualities.put(null, __one);
+        __qualities.put("1.0", __one);
+        __qualities.put("1", __one);
+        __qualities.put("0.9", new Float("0.9"));
+        __qualities.put("0.8", new Float("0.8"));
+        __qualities.put("0.7", new Float("0.7"));
+        __qualities.put("0.66", new Float("0.66"));
+        __qualities.put("0.6", new Float("0.6"));
+        __qualities.put("0.5", new Float("0.5"));
+        __qualities.put("0.4", new Float("0.4"));
+        __qualities.put("0.33", new Float("0.33"));
+        __qualities.put("0.3", new Float("0.3"));
+        __qualities.put("0.2", new Float("0.2"));
+        __qualities.put("0.1", new Float("0.1"));
+        __qualities.put("0", __zero);
+        __qualities.put("0.0", __zero);
+    }
+
+    /* ------------------------------------------------------------ */
+    public static Float getQuality(String value)
+    {
+        if (value == null) return __zero;
+
+        int qe = value.indexOf(";");
+        if (qe++ < 0 || qe == value.length()) return __one;
+
+        if (value.charAt(qe++) == 'q')
+        {
+            qe++;
+            Map.Entry entry = __qualities.getEntry(value, qe, value.length() - qe);
+            if (entry != null) return (Float) entry.getValue();
+        }
+
+        HashMap params = new HashMap(3);
+        valueParameters(value, params);
+        String qs = (String) params.get("q");
+        Float q = (Float) __qualities.get(qs);
+        if (q == null)
+        {
+            try
+            {
+                q = new Float(qs);
+            }
+            catch (Exception e)
+            {
+                q = __one;
+            }
+        }
+        return q;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * List values in quality order.
+     * 
+     * @param e Enumeration of values with quality parameters
+     * @return values in quality order.
+     */
+    public static List qualityList(Enumeration e)
+    {
+        if (e == null || !e.hasMoreElements()) return Collections.EMPTY_LIST;
+
+        Object list = null;
+        Object qual = null;
+
+        // Assume list will be well ordered and just add nonzero
+        while (e.hasMoreElements())
+        {
+            String v = e.nextElement().toString();
+            Float q = getQuality(v);
+
+            if (q.floatValue() >= 0.001)
+            {
+                list = LazyList.add(list, v);
+                qual = LazyList.add(qual, q);
+            }
+        }
+
+        List vl = LazyList.getList(list, false);
+        if (vl.size() < 2) return vl;
+
+        List ql = LazyList.getList(qual, false);
+
+        // sort list with swaps
+        Float last = __zero;
+        for (int i = vl.size(); i-- > 0;)
+        {
+            Float q = (Float) ql.get(i);
+            if (last.compareTo(q) > 0)
+            {
+                Object tmp = vl.get(i);
+                vl.set(i, vl.get(i + 1));
+                vl.set(i + 1, tmp);
+                ql.set(i, ql.get(i + 1));
+                ql.set(i + 1, q);
+                last = __zero;
+                i = vl.size();
+                continue;
+            }
+            last = q;
+        }
+        ql.clear();
+        return vl;
+    }
+
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    public static final class Field
+    {
+        private Buffer _name;
+        private Buffer _value;
+        private Field _next;
+
+        /* ------------------------------------------------------------ */
+        private Field(Buffer name, Buffer value)
+        {
+            _name = name;
+            _value = value;
+            _next = null;
+        }
+        
+        /* ------------------------------------------------------------ */
+        public void putTo(Buffer buffer) throws IOException
+        {
+            int o=(_name instanceof CachedBuffer)?((CachedBuffer)_name).getOrdinal():-1;
+            if (o>=0)
+                buffer.put(_name);
+            else
+            {
+                int s=_name.getIndex();
+                int e=_name.putIndex();
+                while (s<e)
+                {
+                    byte b=_name.peek(s++);
+                    switch(b)
+                    {
+                        case '\r':
+                        case '\n':
+                        case ':' :
+                            continue;
+                        default:
+                            buffer.put(b);
+                    }
+                }
+            }
+            
+            buffer.put((byte) ':');
+            buffer.put((byte) ' ');
+            
+            o=(_value instanceof CachedBuffer)?((CachedBuffer)_value).getOrdinal():-1;
+            if (o>=0)
+                buffer.put(_value);
+            else
+            {
+                int s=_value.getIndex();
+                int e=_value.putIndex();
+                while (s<e)
+                {
+                    byte b=_value.peek(s++);
+                    switch(b)
+                    {
+                        case '\r':
+                        case '\n':
+                            continue;
+                        default:
+                            buffer.put(b);
+                    }
+                }
+            }
+
+            BufferUtil.putCRLF(buffer);
+        }
+
+        /* ------------------------------------------------------------ */
+        public String getName()
+        {
+            return BufferUtil.to8859_1_String(_name);
+        }
+
+        /* ------------------------------------------------------------ */
+        Buffer getNameBuffer()
+        {
+            return _name;
+        }
+
+        /* ------------------------------------------------------------ */
+        public int getNameOrdinal()
+        {
+            return HttpHeaders.CACHE.getOrdinal(_name);
+        }
+
+        /* ------------------------------------------------------------ */
+        public String getValue()
+        {
+            return BufferUtil.to8859_1_String(_value);
+        }
+
+        /* ------------------------------------------------------------ */
+        public Buffer getValueBuffer()
+        {
+            return _value;
+        }
+
+        /* ------------------------------------------------------------ */
+        public int getValueOrdinal()
+        {
+            return HttpHeaderValues.CACHE.getOrdinal(_value);
+        }
+
+        /* ------------------------------------------------------------ */
+        public int getIntValue()
+        {
+            return (int) getLongValue();
+        }
+
+        /* ------------------------------------------------------------ */
+        public long getLongValue()
+        {
+            return BufferUtil.toLong(_value);
+        }
+
+        /* ------------------------------------------------------------ */
+        public String toString()
+        {
+            return ("[" + getName() + "=" + _value + (_next == null ? "" : "->") + "]");
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/http/HttpGenerator.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,1092 @@
+//
+//  ========================================================================
+//  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.http;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+
+import org.eclipse.jetty.io.Buffer;
+import org.eclipse.jetty.io.BufferCache.CachedBuffer;
+import org.eclipse.jetty.io.BufferUtil;
+import org.eclipse.jetty.io.Buffers;
+import org.eclipse.jetty.io.ByteArrayBuffer;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.EofException;
+import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/* ------------------------------------------------------------ */
+/**
+ * HttpGenerator. Builds HTTP Messages.
+ *
+ *
+ *
+ */
+public class HttpGenerator extends AbstractGenerator
+{
+    private static final Logger LOG = Log.getLogger(HttpGenerator.class);
+
+    // Build cache of response lines for status
+    private static class Status
+    {
+        Buffer _reason;
+        Buffer _schemeCode;
+        Buffer _responseLine;
+    }
+    private static final Status[] __status = new Status[HttpStatus.MAX_CODE+1];
+    static
+    {
+        int versionLength=HttpVersions.HTTP_1_1_BUFFER.length();
+
+        for (int i=0;i<__status.length;i++)
+        {
+            HttpStatus.Code code = HttpStatus.getCode(i);
+            if (code==null)
+                continue;
+            String reason=code.getMessage();
+            byte[] bytes=new byte[versionLength+5+reason.length()+2];
+            HttpVersions.HTTP_1_1_BUFFER.peek(0,bytes, 0, versionLength);
+            bytes[versionLength+0]=' ';
+            bytes[versionLength+1]=(byte)('0'+i/100);
+            bytes[versionLength+2]=(byte)('0'+(i%100)/10);
+            bytes[versionLength+3]=(byte)('0'+(i%10));
+            bytes[versionLength+4]=' ';
+            for (int j=0;j<reason.length();j++)
+                bytes[versionLength+5+j]=(byte)reason.charAt(j);
+            bytes[versionLength+5+reason.length()]=HttpTokens.CARRIAGE_RETURN;
+            bytes[versionLength+6+reason.length()]=HttpTokens.LINE_FEED;
+
+            __status[i] = new Status();
+            __status[i]._reason=new ByteArrayBuffer(bytes,versionLength+5,bytes.length-versionLength-7,Buffer.IMMUTABLE);
+            __status[i]._schemeCode=new ByteArrayBuffer(bytes,0,versionLength+5,Buffer.IMMUTABLE);
+            __status[i]._responseLine=new ByteArrayBuffer(bytes,0,bytes.length,Buffer.IMMUTABLE);
+        }
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    public static Buffer getReasonBuffer(int code)
+    {
+        Status status = code<__status.length?__status[code]:null;
+        if (status!=null)
+            return status._reason;
+        return null;
+    }
+
+
+    // common _content
+    private static final byte[] LAST_CHUNK =
+    { (byte) '0', (byte) '\015', (byte) '\012', (byte) '\015', (byte) '\012'};
+    private static final byte[] CONTENT_LENGTH_0 = StringUtil.getBytes("Content-Length: 0\015\012");
+    private static final byte[] CONNECTION_KEEP_ALIVE = StringUtil.getBytes("Connection: keep-alive\015\012");
+    private static final byte[] CONNECTION_CLOSE = StringUtil.getBytes("Connection: close\015\012");
+    private static final byte[] CONNECTION_ = StringUtil.getBytes("Connection: ");
+    private static final byte[] CRLF = StringUtil.getBytes("\015\012");
+    private static final byte[] TRANSFER_ENCODING_CHUNKED = StringUtil.getBytes("Transfer-Encoding: chunked\015\012");
+    private static byte[] SERVER = StringUtil.getBytes("Server: Jetty(7.0.x)\015\012");
+
+    // other statics
+    private static final int CHUNK_SPACE = 12;
+
+    public static void setServerVersion(String version)
+    {
+        SERVER=StringUtil.getBytes("Server: Jetty("+version+")\015\012");
+    }
+
+    // data
+    protected boolean _bypass = false; // True if _content buffer can be written directly to endp and bypass the content buffer
+    private boolean _needCRLF = false;
+    private boolean _needEOC = false;
+    private boolean _bufferChunked = false;
+
+
+    /* ------------------------------------------------------------------------------- */
+    /**
+     * Constructor.
+     *
+     * @param buffers buffer pool
+     * @param io the end point to use
+     */
+    public HttpGenerator(Buffers buffers, EndPoint io)
+    {
+        super(buffers,io);
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    @Override
+    public void reset()
+    {
+        if (_persistent!=null && !_persistent && _endp!=null && !_endp.isOutputShutdown())
+        {
+            try
+            {
+                _endp.shutdownOutput();
+            }
+            catch(IOException e)
+            {
+                LOG.ignore(e);
+            }
+        }
+        super.reset();
+        if (_buffer!=null)
+            _buffer.clear();
+        if (_header!=null)
+            _header.clear();
+        if (_content!=null)
+            _content=null;
+        _bypass = false;
+        _needCRLF = false;
+        _needEOC = false;
+        _bufferChunked=false;
+        _method=null;
+        _uri=null;
+        _noContent=false;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Add content.
+     *
+     * @param content
+     * @param last
+     * @throws IllegalArgumentException if <code>content</code> is {@link Buffer#isImmutable immutable}.
+     * @throws IllegalStateException If the request is not expecting any more content,
+     *   or if the buffers are full and cannot be flushed.
+     * @throws IOException if there is a problem flushing the buffers.
+     */
+    public void addContent(Buffer content, boolean last) throws IOException
+    {
+        if (_noContent)
+            throw new IllegalStateException("NO CONTENT");
+
+        if (_last || _state==STATE_END)
+        {
+            LOG.warn("Ignoring extra content {}",content);
+            content.clear();
+            return;
+        }
+        _last = last;
+
+        // Handle any unfinished business?
+        if (_content!=null && _content.length()>0 || _bufferChunked)
+        {
+            if (_endp.isOutputShutdown())
+                throw new EofException();
+            flushBuffer();
+            if (_content != null && _content.length()>0)
+            {
+                if (_bufferChunked)
+                {
+                    Buffer nc=_buffers.getBuffer(_content.length()+CHUNK_SPACE+content.length());
+                    nc.put(_content);
+                    nc.put(HttpTokens.CRLF);
+                    BufferUtil.putHexInt(nc, content.length());
+                    nc.put(HttpTokens.CRLF);
+                    nc.put(content);
+                    content=nc;
+                }
+                else
+                {
+                    Buffer nc=_buffers.getBuffer(_content.length()+content.length());
+                    nc.put(_content);
+                    nc.put(content);
+                    content=nc;
+                }
+            }
+        }
+
+        _content = content;
+        _contentWritten += content.length();
+
+        // Handle the _content
+        if (_head)
+        {
+            content.clear();
+            _content=null;
+        }
+        else if (_endp != null && (_buffer==null || _buffer.length()==0) && _content.length() > 0 && (_last || isCommitted() && _content.length()>1024))
+        {
+            _bypass = true;
+        }
+        else if (!_bufferChunked)
+        {
+            // Yes - so we better check we have a buffer
+            if (_buffer == null)
+                _buffer = _buffers.getBuffer();
+
+            // Copy _content to buffer;
+            int len=_buffer.put(_content);
+            _content.skip(len);
+            if (_content.length() == 0)
+                _content = null;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * send complete response.
+     *
+     * @param response
+     */
+    public void sendResponse(Buffer response) throws IOException
+    {
+        if (_noContent || _state!=STATE_HEADER || _content!=null && _content.length()>0 || _bufferChunked || _head )
+            throw new IllegalStateException();
+
+        _last = true;
+
+        _content = response;
+        _bypass = true;
+        _state = STATE_FLUSHING;
+
+        // TODO this is not exactly right, but should do.
+        _contentLength =_contentWritten = response.length();
+
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Prepare buffer for unchecked writes.
+     * Prepare the generator buffer to receive unchecked writes
+     * @return the available space in the buffer.
+     * @throws IOException
+     */
+    @Override
+    public int prepareUncheckedAddContent() throws IOException
+    {
+        if (_noContent)
+            return -1;
+
+        if (_last || _state==STATE_END)
+            return -1;
+
+        // Handle any unfinished business?
+        Buffer content = _content;
+        if (content != null && content.length()>0 || _bufferChunked)
+        {
+            flushBuffer();
+            if (content != null && content.length()>0 || _bufferChunked)
+                throw new IllegalStateException("FULL");
+        }
+
+        // we better check we have a buffer
+        if (_buffer == null)
+            _buffer = _buffers.getBuffer();
+
+        _contentWritten-=_buffer.length();
+
+        // Handle the _content
+        if (_head)
+            return Integer.MAX_VALUE;
+
+        return _buffer.space()-(_contentLength == HttpTokens.CHUNKED_CONTENT?CHUNK_SPACE:0);
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public boolean isBufferFull()
+    {
+        // Should we flush the buffers?
+        return super.isBufferFull() || _bufferChunked || _bypass  || (_contentLength == HttpTokens.CHUNKED_CONTENT && _buffer != null && _buffer.space() < CHUNK_SPACE);
+    }
+
+    /* ------------------------------------------------------------ */
+    public void send1xx(int code) throws IOException
+    {
+        if (_state != STATE_HEADER)
+            return;
+
+        if (code<100||code>199)
+            throw new IllegalArgumentException("!1xx");
+        Status status=__status[code];
+        if (status==null)
+            throw new IllegalArgumentException(code+"?");
+
+        // get a header buffer
+        if (_header == null)
+            _header = _buffers.getHeader();
+
+        _header.put(status._responseLine);
+        _header.put(HttpTokens.CRLF);
+
+        try
+        {
+            // nasty semi busy flush!
+            while(_header.length()>0)
+            {
+                int len = _endp.flush(_header);
+                if (len<0)
+                    throw new EofException();
+                if (len==0)
+                    Thread.sleep(100);
+            }
+        }
+        catch(InterruptedException e)
+        {
+            LOG.debug(e);
+            throw new InterruptedIOException(e.toString());
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public boolean isRequest()
+    {
+        return _method!=null;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public boolean isResponse()
+    {
+        return _method==null;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void completeHeader(HttpFields fields, boolean allContentAdded) throws IOException
+    {
+        if (_state != STATE_HEADER)
+            return;
+
+        // handle a reset
+        if (isResponse() && _status==0)
+            throw new EofException();
+
+        if (_last && !allContentAdded)
+            throw new IllegalStateException("last?");
+        _last = _last | allContentAdded;
+
+        // get a header buffer
+        if (_header == null)
+            _header = _buffers.getHeader();
+
+        boolean has_server = false;
+
+        try
+        {
+            if (isRequest())
+            {
+                _persistent=true;
+
+                if (_version == HttpVersions.HTTP_0_9_ORDINAL)
+                {
+                    _contentLength = HttpTokens.NO_CONTENT;
+                    _header.put(_method);
+                    _header.put((byte)' ');
+                    _header.put(_uri.getBytes("UTF-8")); // TODO check
+                    _header.put(HttpTokens.CRLF);
+                    _state = STATE_FLUSHING;
+                    _noContent=true;
+                    return;
+                }
+                else
+                {
+                    _header.put(_method);
+                    _header.put((byte)' ');
+                    _header.put(_uri.getBytes("UTF-8")); // TODO check
+                    _header.put((byte)' ');
+                    _header.put(_version==HttpVersions.HTTP_1_0_ORDINAL?HttpVersions.HTTP_1_0_BUFFER:HttpVersions.HTTP_1_1_BUFFER);
+                    _header.put(HttpTokens.CRLF);
+                }
+            }
+            else
+            {
+                // Responses
+                if (_version == HttpVersions.HTTP_0_9_ORDINAL)
+                {
+                    _persistent = false;
+                    _contentLength = HttpTokens.EOF_CONTENT;
+                    _state = STATE_CONTENT;
+                    return;
+                }
+                else
+                {
+                    if (_persistent==null)
+                        _persistent= (_version > HttpVersions.HTTP_1_0_ORDINAL);
+
+                    // add response line
+                    Status status = _status<__status.length?__status[_status]:null;
+
+                    if (status==null)
+                    {
+                        _header.put(HttpVersions.HTTP_1_1_BUFFER);
+                        _header.put((byte) ' ');
+                        _header.put((byte) ('0' + _status / 100));
+                        _header.put((byte) ('0' + (_status % 100) / 10));
+                        _header.put((byte) ('0' + (_status % 10)));
+                        _header.put((byte) ' ');
+                        if (_reason==null)
+                        {
+                            _header.put((byte) ('0' + _status / 100));
+                            _header.put((byte) ('0' + (_status % 100) / 10));
+                            _header.put((byte) ('0' + (_status % 10)));
+                        }
+                        else
+                            _header.put(_reason);
+                        _header.put(HttpTokens.CRLF);
+                    }
+                    else
+                    {
+                        if (_reason==null)
+                            _header.put(status._responseLine);
+                        else
+                        {
+                            _header.put(status._schemeCode);
+                            _header.put(_reason);
+                            _header.put(HttpTokens.CRLF);
+                        }
+                    }
+
+                    if (_status<200 && _status>=100 )
+                    {
+                        _noContent=true;
+                        _content=null;
+                        if (_buffer!=null)
+                            _buffer.clear();
+                        // end the header.
+
+                        if (_status!=101 )
+                        {
+                            _header.put(HttpTokens.CRLF);
+                            _state = STATE_CONTENT;
+                            return;
+                        }
+                    }
+                    else if (_status==204 || _status==304)
+                    {
+                        _noContent=true;
+                        _content=null;
+                        if (_buffer!=null)
+                            _buffer.clear();
+                    }
+                }
+            }
+
+            // Add headers
+            if (_status>=200 && _date!=null)
+            {
+                _header.put(HttpHeaders.DATE_BUFFER);
+                _header.put((byte)':');
+                _header.put((byte)' ');
+                _header.put(_date);
+                _header.put(CRLF);
+            }
+
+            // key field values
+            HttpFields.Field content_length = null;
+            HttpFields.Field transfer_encoding = null;
+            boolean keep_alive = false;
+            boolean close=false;
+            boolean content_type=false;
+            StringBuilder connection = null;
+
+            if (fields != null)
+            {
+                int s=fields.size();
+                for (int f=0;f<s;f++)
+                {
+                    HttpFields.Field field = fields.getField(f);
+                    if (field==null)
+                        continue;
+
+                    switch (field.getNameOrdinal())
+                    {
+                        case HttpHeaders.CONTENT_LENGTH_ORDINAL:
+                            content_length = field;
+                            _contentLength = field.getLongValue();
+
+                            if (_contentLength < _contentWritten || _last && _contentLength != _contentWritten)
+                                content_length = null;
+
+                            // write the field to the header buffer
+                            field.putTo(_header);
+                            break;
+
+                        case HttpHeaders.CONTENT_TYPE_ORDINAL:
+                            if (BufferUtil.isPrefix(MimeTypes.MULTIPART_BYTERANGES_BUFFER, field.getValueBuffer())) _contentLength = HttpTokens.SELF_DEFINING_CONTENT;
+
+                            // write the field to the header buffer
+                            content_type=true;
+                            field.putTo(_header);
+                            break;
+
+                        case HttpHeaders.TRANSFER_ENCODING_ORDINAL:
+                            if (_version == HttpVersions.HTTP_1_1_ORDINAL)
+                                transfer_encoding = field;
+                            // Do NOT add yet!
+                            break;
+
+                        case HttpHeaders.CONNECTION_ORDINAL:
+                            if (isRequest())
+                                field.putTo(_header);
+
+                            int connection_value = field.getValueOrdinal();
+                            switch (connection_value)
+                            {
+                                case -1:
+                                {
+                                    String[] values = field.getValue().split(",");
+                                    for  (int i=0;values!=null && i<values.length;i++)
+                                    {
+                                        CachedBuffer cb = HttpHeaderValues.CACHE.get(values[i].trim());
+
+                                        if (cb!=null)
+                                        {
+                                            switch(cb.getOrdinal())
+                                            {
+                                                case HttpHeaderValues.CLOSE_ORDINAL:
+                                                    close=true;
+                                                    if (isResponse())
+                                                        _persistent=false;
+                                                    keep_alive=false;
+                                                    if (!_persistent && isResponse() && _contentLength == HttpTokens.UNKNOWN_CONTENT)
+                                                        _contentLength = HttpTokens.EOF_CONTENT;
+                                                    break;
+
+                                                case HttpHeaderValues.KEEP_ALIVE_ORDINAL:
+                                                    if (_version == HttpVersions.HTTP_1_0_ORDINAL)
+                                                    {
+                                                        keep_alive = true;
+                                                        if (isResponse())
+                                                            _persistent = true;
+                                                    }
+                                                    break;
+
+                                                default:
+                                                    if (connection==null)
+                                                        connection=new StringBuilder();
+                                                    else
+                                                        connection.append(',');
+                                                    connection.append(values[i]);
+                                            }
+                                        }
+                                        else
+                                        {
+                                            if (connection==null)
+                                                connection=new StringBuilder();
+                                            else
+                                                connection.append(',');
+                                            connection.append(values[i]);
+                                        }
+                                    }
+
+                                    break;
+                                }
+                                case HttpHeaderValues.UPGRADE_ORDINAL:
+                                {
+                                    // special case for websocket connection ordering
+                                    if (isResponse())
+                                    {
+                                        field.putTo(_header);
+                                        continue;
+                                    }
+                                }
+                                case HttpHeaderValues.CLOSE_ORDINAL:
+                                {
+                                    close=true;
+                                    if (isResponse())
+                                        _persistent=false;
+                                    if (!_persistent && isResponse() && _contentLength == HttpTokens.UNKNOWN_CONTENT)
+                                        _contentLength = HttpTokens.EOF_CONTENT;
+                                    break;
+                                }
+                                case HttpHeaderValues.KEEP_ALIVE_ORDINAL:
+                                {
+                                    if (_version == HttpVersions.HTTP_1_0_ORDINAL)
+                                    {
+                                        keep_alive = true;
+                                        if (isResponse())
+                                            _persistent=true;
+                                    }
+                                    break;
+                                }
+                                default:
+                                {
+                                    if (connection==null)
+                                        connection=new StringBuilder();
+                                    else
+                                        connection.append(',');
+                                    connection.append(field.getValue());
+                                }
+                            }
+
+                            // Do NOT add yet!
+                            break;
+
+                        case HttpHeaders.SERVER_ORDINAL:
+                            if (getSendServerVersion())
+                            {
+                                has_server=true;
+                                field.putTo(_header);
+                            }
+                            break;
+
+                        default:
+                            // write the field to the header buffer
+                            field.putTo(_header);
+                    }
+                }
+            }
+
+            // Calculate how to end _content and connection, _content length and transfer encoding
+            // settings.
+            // From RFC 2616 4.4:
+            // 1. No body for 1xx, 204, 304 & HEAD response
+            // 2. Force _content-length?
+            // 3. If Transfer-Encoding!=identity && HTTP/1.1 && !HttpConnection==close then chunk
+            // 4. Content-Length
+            // 5. multipart/byteranges
+            // 6. close
+            switch ((int) _contentLength)
+            {
+                case HttpTokens.UNKNOWN_CONTENT:
+                    // It may be that we have no _content, or perhaps _content just has not been
+                    // written yet?
+
+                    // Response known not to have a body
+                    if (_contentWritten == 0 && isResponse() && (_status < 200 || _status == 204 || _status == 304))
+                        _contentLength = HttpTokens.NO_CONTENT;
+                    else if (_last)
+                    {
+                        // we have seen all the _content there is
+                        _contentLength = _contentWritten;
+                        if (content_length == null && (isResponse() || _contentLength>0 || content_type ) && !_noContent)
+                        {
+                            // known length but not actually set.
+                            _header.put(HttpHeaders.CONTENT_LENGTH_BUFFER);
+                            _header.put(HttpTokens.COLON);
+                            _header.put((byte) ' ');
+                            BufferUtil.putDecLong(_header, _contentLength);
+                            _header.put(HttpTokens.CRLF);
+                        }
+                    }
+                    else
+                    {
+                        // No idea, so we must assume that a body is coming
+                        _contentLength = (!_persistent || _version < HttpVersions.HTTP_1_1_ORDINAL ) ? HttpTokens.EOF_CONTENT : HttpTokens.CHUNKED_CONTENT;
+                        if (isRequest() && _contentLength==HttpTokens.EOF_CONTENT)
+                        {
+                            _contentLength=HttpTokens.NO_CONTENT;
+                            _noContent=true;
+                        }
+                    }
+                    break;
+
+                case HttpTokens.NO_CONTENT:
+                    if (content_length == null && isResponse() && _status >= 200 && _status != 204 && _status != 304)
+                        _header.put(CONTENT_LENGTH_0);
+                    break;
+
+                case HttpTokens.EOF_CONTENT:
+                    _persistent = isRequest();
+                    break;
+
+                case HttpTokens.CHUNKED_CONTENT:
+                    break;
+
+                default:
+                    // TODO - maybe allow forced chunking by setting te ???
+                    break;
+            }
+
+            // Add transfer_encoding if needed
+            if (_contentLength == HttpTokens.CHUNKED_CONTENT)
+            {
+                // try to use user supplied encoding as it may have other values.
+                if (transfer_encoding != null && HttpHeaderValues.CHUNKED_ORDINAL != transfer_encoding.getValueOrdinal())
+                {
+                    String c = transfer_encoding.getValue();
+                    if (c.endsWith(HttpHeaderValues.CHUNKED))
+                        transfer_encoding.putTo(_header);
+                    else
+                        throw new IllegalArgumentException("BAD TE");
+                }
+                else
+                    _header.put(TRANSFER_ENCODING_CHUNKED);
+            }
+
+            // Handle connection if need be
+            if (_contentLength==HttpTokens.EOF_CONTENT)
+            {
+                keep_alive=false;
+                _persistent=false;
+            }
+
+            if (isResponse())
+            {
+                if (!_persistent && (close || _version > HttpVersions.HTTP_1_0_ORDINAL))
+                {
+                    _header.put(CONNECTION_CLOSE);
+                    if (connection!=null)
+                    {
+                        _header.setPutIndex(_header.putIndex()-2);
+                        _header.put((byte)',');
+                        _header.put(connection.toString().getBytes());
+                        _header.put(CRLF);
+                    }
+                }
+                else if (keep_alive)
+                {
+                    _header.put(CONNECTION_KEEP_ALIVE);
+                    if (connection!=null)
+                    {
+                        _header.setPutIndex(_header.putIndex()-2);
+                        _header.put((byte)',');
+                        _header.put(connection.toString().getBytes());
+                        _header.put(CRLF);
+                    }
+                }
+                else if (connection!=null)
+                {
+                    _header.put(CONNECTION_);
+                    _header.put(connection.toString().getBytes());
+                    _header.put(CRLF);
+                }
+            }
+
+            if (!has_server && _status>199 && getSendServerVersion())
+                _header.put(SERVER);
+
+            // end the header.
+            _header.put(HttpTokens.CRLF);
+            _state = STATE_CONTENT;
+
+        }
+        catch(ArrayIndexOutOfBoundsException e)
+        {
+            throw new RuntimeException("Header>"+_header.capacity(),e);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Complete the message.
+     *
+     * @throws IOException
+     */
+    @Override
+    public void complete() throws IOException
+    {
+        if (_state == STATE_END)
+            return;
+
+        super.complete();
+
+        if (_state < STATE_FLUSHING)
+        {
+            _state = STATE_FLUSHING;
+            if (_contentLength == HttpTokens.CHUNKED_CONTENT)
+                _needEOC = true;
+        }
+
+        flushBuffer();
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public int flushBuffer() throws IOException
+    {
+        try
+        {
+
+            if (_state == STATE_HEADER)
+                throw new IllegalStateException("State==HEADER");
+
+            prepareBuffers();
+
+            if (_endp == null)
+            {
+                if (_needCRLF && _buffer!=null)
+                    _buffer.put(HttpTokens.CRLF);
+                if (_needEOC && _buffer!=null && !_head)
+                    _buffer.put(LAST_CHUNK);
+                _needCRLF=false;
+                _needEOC=false;
+                return 0;
+            }
+
+            int total= 0;
+
+            int len = -1;
+            int to_flush = flushMask();
+            int last_flush;
+
+            do
+            {
+                last_flush=to_flush;
+                switch (to_flush)
+                {
+                    case 7:
+                        throw new IllegalStateException(); // should never happen!
+                    case 6:
+                        len = _endp.flush(_header, _buffer, null);
+                        break;
+                    case 5:
+                        len = _endp.flush(_header, _content, null);
+                        break;
+                    case 4:
+                        len = _endp.flush(_header);
+                        break;
+                    case 3:
+                        len = _endp.flush(_buffer, _content, null);
+                        break;
+                    case 2:
+                        len = _endp.flush(_buffer);
+                        break;
+                    case 1:
+                        len = _endp.flush(_content);
+                        break;
+                    case 0:
+                    {
+                        len=0;
+                        // Nothing more we can write now.
+                        if (_header != null)
+                            _header.clear();
+
+                        _bypass = false;
+                        _bufferChunked = false;
+
+                        if (_buffer != null)
+                        {
+                            _buffer.clear();
+                            if (_contentLength == HttpTokens.CHUNKED_CONTENT)
+                            {
+                                // reserve some space for the chunk header
+                                _buffer.setPutIndex(CHUNK_SPACE);
+                                _buffer.setGetIndex(CHUNK_SPACE);
+
+                                // Special case handling for small left over buffer from
+                                // an addContent that caused a buffer flush.
+                                if (_content != null && _content.length() < _buffer.space() && _state != STATE_FLUSHING)
+                                {
+                                    _buffer.put(_content);
+                                    _content.clear();
+                                    _content=null;
+                                }
+                            }
+                        }
+
+                        // Are we completely finished for now?
+                        if (!_needCRLF && !_needEOC && (_content==null || _content.length()==0))
+                        {
+                            if (_state == STATE_FLUSHING)
+                                _state = STATE_END;
+
+                            if (_state==STATE_END && _persistent != null && !_persistent && _status!=100 && _method==null)
+                                _endp.shutdownOutput();
+                        }
+                        else
+                            // Try to prepare more to write.
+                            prepareBuffers();
+                    }
+
+                }
+
+                if (len > 0)
+                    total+=len;
+
+                to_flush = flushMask();
+            }
+            // loop while progress is being made (OR we have prepared some buffers that might make progress)
+            while (len>0 || (to_flush!=0 && last_flush==0));
+
+            return total;
+        }
+        catch (IOException e)
+        {
+            LOG.ignore(e);
+            throw (e instanceof EofException) ? e:new EofException(e);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    private int flushMask()
+    {
+        return  ((_header != null && _header.length() > 0)?4:0)
+        | ((_buffer != null && _buffer.length() > 0)?2:0)
+        | ((_bypass && _content != null && _content.length() > 0)?1:0);
+    }
+
+    /* ------------------------------------------------------------ */
+    private void prepareBuffers()
+    {
+        // if we are not flushing an existing chunk
+        if (!_bufferChunked)
+        {
+            // Refill buffer if possible
+            if (!_bypass && _content != null && _content.length() > 0 && _buffer != null && _buffer.space() > 0)
+            {
+                int len = _buffer.put(_content);
+                _content.skip(len);
+                if (_content.length() == 0)
+                    _content = null;
+            }
+
+            // Chunk buffer if need be
+            if (_contentLength == HttpTokens.CHUNKED_CONTENT)
+            {
+                if (_bypass && (_buffer==null||_buffer.length()==0) && _content!=null)
+                {
+                    // this is a bypass write
+                    int size = _content.length();
+                    _bufferChunked = true;
+
+                    if (_header == null)
+                        _header = _buffers.getHeader();
+
+                    // if we need CRLF add this to header
+                    if (_needCRLF)
+                    {
+                        if (_header.length() > 0) throw new IllegalStateException("EOC");
+                        _header.put(HttpTokens.CRLF);
+                        _needCRLF = false;
+                    }
+                    // Add the chunk size to the header
+                    BufferUtil.putHexInt(_header, size);
+                    _header.put(HttpTokens.CRLF);
+
+                    // Need a CRLF after the content
+                    _needCRLF=true;
+                }
+                else if (_buffer!=null)
+                {
+                    int size = _buffer.length();
+                    if (size > 0)
+                    {
+                        // Prepare a chunk!
+                        _bufferChunked = true;
+
+                        // Did we leave space at the start of the buffer.
+                        //noinspection ConstantConditions
+                        if (_buffer.getIndex() == CHUNK_SPACE)
+                        {
+                            // Oh yes, goodie! let's use it then!
+                            _buffer.poke(_buffer.getIndex() - 2, HttpTokens.CRLF, 0, 2);
+                            _buffer.setGetIndex(_buffer.getIndex() - 2);
+                            BufferUtil.prependHexInt(_buffer, size);
+
+                            if (_needCRLF)
+                            {
+                                _buffer.poke(_buffer.getIndex() - 2, HttpTokens.CRLF, 0, 2);
+                                _buffer.setGetIndex(_buffer.getIndex() - 2);
+                                _needCRLF = false;
+                            }
+                        }
+                        else
+                        {
+                            // No space so lets use a header buffer.
+                            if (_header == null)
+                                _header = _buffers.getHeader();
+
+                            if (_needCRLF)
+                            {
+                                if (_header.length() > 0) throw new IllegalStateException("EOC");
+                                _header.put(HttpTokens.CRLF);
+                                _needCRLF = false;
+                            }
+                            BufferUtil.putHexInt(_header, size);
+                            _header.put(HttpTokens.CRLF);
+                        }
+
+                        // Add end chunk trailer.
+                        if (_buffer.space() >= 2)
+                            _buffer.put(HttpTokens.CRLF);
+                        else
+                            _needCRLF = true;
+                    }
+                }
+
+                // If we need EOC and everything written
+                if (_needEOC && (_content == null || _content.length() == 0))
+                {
+                    if (_header == null && _buffer == null)
+                        _header = _buffers.getHeader();
+
+                    if (_needCRLF)
+                    {
+                        if (_buffer == null && _header != null && _header.space() >= HttpTokens.CRLF.length)
+                        {
+                            _header.put(HttpTokens.CRLF);
+                            _needCRLF = false;
+                        }
+                        else if (_buffer!=null && _buffer.space() >= HttpTokens.CRLF.length)
+                        {
+                            _buffer.put(HttpTokens.CRLF);
+                            _needCRLF = false;
+                        }
+                    }
+
+                    if (!_needCRLF && _needEOC)
+                    {
+                        if (_buffer == null && _header != null && _header.space() >= LAST_CHUNK.length)
+                        {
+                            if (!_head)
+                            {
+                                _header.put(LAST_CHUNK);
+                                _bufferChunked=true;
+                            }
+                            _needEOC = false;
+                        }
+                        else if (_buffer!=null && _buffer.space() >= LAST_CHUNK.length)
+                        {
+                            if (!_head)
+                            {
+                                _buffer.put(LAST_CHUNK);
+                                _bufferChunked=true;
+                            }
+                            _needEOC = false;
+                        }
+                    }
+                }
+            }
+        }
+
+        if (_content != null && _content.length() == 0)
+            _content = null;
+
+    }
+
+    public int getBytesBuffered()
+    {
+        return(_header==null?0:_header.length())+
+        (_buffer==null?0:_buffer.length())+
+        (_content==null?0:_content.length());
+    }
+
+    public boolean isEmpty()
+    {
+        return (_header==null||_header.length()==0) &&
+        (_buffer==null||_buffer.length()==0) &&
+        (_content==null||_content.length()==0);
+    }
+
+    @Override
+    public String toString()
+    {
+        Buffer header=_header;
+        Buffer buffer=_buffer;
+        Buffer content=_content;
+        return String.format("%s{s=%d,h=%d,b=%d,c=%d}",
+                getClass().getSimpleName(),
+                _state,
+                header == null ? -1 : header.length(),
+                buffer == null ? -1 : buffer.length(),
+                content == null ? -1 : content.length());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/http/HttpHeaderValues.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,88 @@
+//
+//  ========================================================================
+//  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.http;
+
+import org.eclipse.jetty.io.Buffer;
+import org.eclipse.jetty.io.BufferCache;
+import org.eclipse.jetty.io.ByteArrayBuffer;
+
+/**
+ * Cached HTTP Header values.
+ * This class caches the conversion of common HTTP Header values to and from {@link ByteArrayBuffer} instances.
+ * The resource "/org/eclipse/jetty/useragents" is checked for a list of common user agents, so that repeated
+ * creation of strings for these agents can be avoided.
+ * 
+ * 
+ */
+public class HttpHeaderValues extends BufferCache
+{
+    public final static String
+        CLOSE="close",
+        CHUNKED="chunked",
+        GZIP="gzip",
+        IDENTITY="identity",
+        KEEP_ALIVE="keep-alive",
+        CONTINUE="100-continue",
+        PROCESSING="102-processing",
+        TE="TE",
+        BYTES="bytes",
+        NO_CACHE="no-cache",
+        UPGRADE="Upgrade";
+
+    public final static int
+        CLOSE_ORDINAL=1,
+        CHUNKED_ORDINAL=2,
+        GZIP_ORDINAL=3,
+        IDENTITY_ORDINAL=4,
+        KEEP_ALIVE_ORDINAL=5,
+        CONTINUE_ORDINAL=6,
+        PROCESSING_ORDINAL=7,
+        TE_ORDINAL=8,
+        BYTES_ORDINAL=9,
+        NO_CACHE_ORDINAL=10,
+        UPGRADE_ORDINAL=11;
+    
+    public final static HttpHeaderValues CACHE= new HttpHeaderValues();
+
+    public final static Buffer 
+        CLOSE_BUFFER=CACHE.add(CLOSE,CLOSE_ORDINAL),
+        CHUNKED_BUFFER=CACHE.add(CHUNKED,CHUNKED_ORDINAL),
+        GZIP_BUFFER=CACHE.add(GZIP,GZIP_ORDINAL),
+        IDENTITY_BUFFER=CACHE.add(IDENTITY,IDENTITY_ORDINAL),
+        KEEP_ALIVE_BUFFER=CACHE.add(KEEP_ALIVE,KEEP_ALIVE_ORDINAL),
+        CONTINUE_BUFFER=CACHE.add(CONTINUE, CONTINUE_ORDINAL),
+        PROCESSING_BUFFER=CACHE.add(PROCESSING, PROCESSING_ORDINAL),
+        TE_BUFFER=CACHE.add(TE,TE_ORDINAL),
+        BYTES_BUFFER=CACHE.add(BYTES,BYTES_ORDINAL),
+        NO_CACHE_BUFFER=CACHE.add(NO_CACHE,NO_CACHE_ORDINAL),
+        UPGRADE_BUFFER=CACHE.add(UPGRADE,UPGRADE_ORDINAL);
+        
+
+    public static boolean hasKnownValues(int httpHeaderOrdinal)
+    {
+        switch(httpHeaderOrdinal)
+        {
+            case HttpHeaders.CONNECTION_ORDINAL:
+            case HttpHeaders.TRANSFER_ENCODING_ORDINAL:
+            case HttpHeaders.CONTENT_ENCODING_ORDINAL:
+                return true;
+        }
+        return false;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/http/HttpHeaders.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,241 @@
+//
+//  ========================================================================
+//  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.http;
+
+import org.eclipse.jetty.io.Buffer;
+import org.eclipse.jetty.io.BufferCache;
+
+/* ------------------------------------------------------------------------------- */
+/** 
+ */
+public class HttpHeaders extends BufferCache
+{
+    /* ------------------------------------------------------------ */
+    /** General Fields.
+     */
+    public final static String 
+        CONNECTION= "Connection",
+        CACHE_CONTROL= "Cache-Control",
+        DATE= "Date",
+        PRAGMA= "Pragma",
+        PROXY_CONNECTION = "Proxy-Connection",
+        TRAILER= "Trailer",
+        TRANSFER_ENCODING= "Transfer-Encoding",
+        UPGRADE= "Upgrade",
+        VIA= "Via",
+        WARNING= "Warning",
+        NEGOTIATE= "Negotiate";
+
+    /* ------------------------------------------------------------ */
+    /** Entity Fields.
+     */
+    public final static String ALLOW= "Allow",
+        CONTENT_ENCODING= "Content-Encoding",
+        CONTENT_LANGUAGE= "Content-Language",
+        CONTENT_LENGTH= "Content-Length",
+        CONTENT_LOCATION= "Content-Location",
+        CONTENT_MD5= "Content-MD5",
+        CONTENT_RANGE= "Content-Range",
+        CONTENT_TYPE= "Content-Type",
+        EXPIRES= "Expires",
+        LAST_MODIFIED= "Last-Modified";
+
+    /* ------------------------------------------------------------ */
+    /** Request Fields.
+     */
+    public final static String ACCEPT= "Accept",
+        ACCEPT_CHARSET= "Accept-Charset",
+        ACCEPT_ENCODING= "Accept-Encoding",
+        ACCEPT_LANGUAGE= "Accept-Language",
+        AUTHORIZATION= "Authorization",
+        EXPECT= "Expect",
+        FORWARDED= "Forwarded",
+        FROM= "From",
+        HOST= "Host",
+        IF_MATCH= "If-Match",
+        IF_MODIFIED_SINCE= "If-Modified-Since",
+        IF_NONE_MATCH= "If-None-Match",
+        IF_RANGE= "If-Range",
+        IF_UNMODIFIED_SINCE= "If-Unmodified-Since",
+        KEEP_ALIVE= "Keep-Alive",
+        MAX_FORWARDS= "Max-Forwards",
+        PROXY_AUTHORIZATION= "Proxy-Authorization",
+        RANGE= "Range",
+        REQUEST_RANGE= "Request-Range",
+        REFERER= "Referer",
+        TE= "TE",
+        USER_AGENT= "User-Agent",
+        X_FORWARDED_FOR= "X-Forwarded-For",
+        X_FORWARDED_PROTO= "X-Forwarded-Proto",
+        X_FORWARDED_SERVER= "X-Forwarded-Server",
+        X_FORWARDED_HOST= "X-Forwarded-Host";
+
+    /* ------------------------------------------------------------ */
+    /** Response Fields.
+     */
+    public final static String ACCEPT_RANGES= "Accept-Ranges",
+        AGE= "Age",
+        ETAG= "ETag",
+        LOCATION= "Location",
+        PROXY_AUTHENTICATE= "Proxy-Authenticate",
+        RETRY_AFTER= "Retry-After",
+        SERVER= "Server",
+        SERVLET_ENGINE= "Servlet-Engine",
+        VARY= "Vary",
+        WWW_AUTHENTICATE= "WWW-Authenticate";
+
+    /* ------------------------------------------------------------ */
+    /** Other Fields.
+     */
+    public final static String COOKIE= "Cookie",
+        SET_COOKIE= "Set-Cookie",
+        SET_COOKIE2= "Set-Cookie2",
+        MIME_VERSION= "MIME-Version",
+        IDENTITY= "identity";
+
+    public final static int CONNECTION_ORDINAL= 1,
+        DATE_ORDINAL= 2,
+        PRAGMA_ORDINAL= 3,
+        TRAILER_ORDINAL= 4,
+        TRANSFER_ENCODING_ORDINAL= 5,
+        UPGRADE_ORDINAL= 6,
+        VIA_ORDINAL= 7,
+        WARNING_ORDINAL= 8,
+        ALLOW_ORDINAL= 9,
+        CONTENT_ENCODING_ORDINAL= 10,
+        CONTENT_LANGUAGE_ORDINAL= 11,
+        CONTENT_LENGTH_ORDINAL= 12,
+        CONTENT_LOCATION_ORDINAL= 13,
+        CONTENT_MD5_ORDINAL= 14,
+        CONTENT_RANGE_ORDINAL= 15,
+        CONTENT_TYPE_ORDINAL= 16,
+        EXPIRES_ORDINAL= 17,
+        LAST_MODIFIED_ORDINAL= 18,
+        ACCEPT_ORDINAL= 19,
+        ACCEPT_CHARSET_ORDINAL= 20,
+        ACCEPT_ENCODING_ORDINAL= 21,
+        ACCEPT_LANGUAGE_ORDINAL= 22,
+        AUTHORIZATION_ORDINAL= 23,
+        EXPECT_ORDINAL= 24,
+        FORWARDED_ORDINAL= 25,
+        FROM_ORDINAL= 26,
+        HOST_ORDINAL= 27,
+        IF_MATCH_ORDINAL= 28,
+        IF_MODIFIED_SINCE_ORDINAL= 29,
+        IF_NONE_MATCH_ORDINAL= 30,
+        IF_RANGE_ORDINAL= 31,
+        IF_UNMODIFIED_SINCE_ORDINAL= 32,
+        KEEP_ALIVE_ORDINAL= 33,
+        MAX_FORWARDS_ORDINAL= 34,
+        PROXY_AUTHORIZATION_ORDINAL= 35,
+        RANGE_ORDINAL= 36,
+        REQUEST_RANGE_ORDINAL= 37,
+        REFERER_ORDINAL= 38,
+        TE_ORDINAL= 39,
+        USER_AGENT_ORDINAL= 40,
+        X_FORWARDED_FOR_ORDINAL= 41,
+        ACCEPT_RANGES_ORDINAL= 42,
+        AGE_ORDINAL= 43,
+        ETAG_ORDINAL= 44,
+        LOCATION_ORDINAL= 45,
+        PROXY_AUTHENTICATE_ORDINAL= 46,
+        RETRY_AFTER_ORDINAL= 47,
+        SERVER_ORDINAL= 48,
+        SERVLET_ENGINE_ORDINAL= 49,
+        VARY_ORDINAL= 50,
+        WWW_AUTHENTICATE_ORDINAL= 51,
+        COOKIE_ORDINAL= 52,
+        SET_COOKIE_ORDINAL= 53,
+        SET_COOKIE2_ORDINAL= 54,
+        MIME_VERSION_ORDINAL= 55,
+        IDENTITY_ORDINAL= 56,
+        CACHE_CONTROL_ORDINAL=57,
+        PROXY_CONNECTION_ORDINAL=58,
+        X_FORWARDED_PROTO_ORDINAL=59,
+        X_FORWARDED_SERVER_ORDINAL=60,
+        X_FORWARDED_HOST_ORDINAL=61;
+
+    public final static HttpHeaders CACHE= new HttpHeaders();
+    
+    public final static Buffer
+        HOST_BUFFER=CACHE.add(HOST,HOST_ORDINAL),
+        ACCEPT_BUFFER=CACHE.add(ACCEPT,ACCEPT_ORDINAL),
+        ACCEPT_CHARSET_BUFFER=CACHE.add(ACCEPT_CHARSET,ACCEPT_CHARSET_ORDINAL),
+        ACCEPT_ENCODING_BUFFER=CACHE.add(ACCEPT_ENCODING,ACCEPT_ENCODING_ORDINAL),
+        ACCEPT_LANGUAGE_BUFFER=CACHE.add(ACCEPT_LANGUAGE,ACCEPT_LANGUAGE_ORDINAL),
+        
+        CONTENT_LENGTH_BUFFER=CACHE.add(CONTENT_LENGTH,CONTENT_LENGTH_ORDINAL),
+        CONNECTION_BUFFER=CACHE.add(CONNECTION,CONNECTION_ORDINAL),
+        CACHE_CONTROL_BUFFER=CACHE.add(CACHE_CONTROL,CACHE_CONTROL_ORDINAL),
+        DATE_BUFFER=CACHE.add(DATE,DATE_ORDINAL),
+        PRAGMA_BUFFER=CACHE.add(PRAGMA,PRAGMA_ORDINAL),
+        TRAILER_BUFFER=CACHE.add(TRAILER,TRAILER_ORDINAL),
+        TRANSFER_ENCODING_BUFFER=CACHE.add(TRANSFER_ENCODING,TRANSFER_ENCODING_ORDINAL),
+        UPGRADE_BUFFER=CACHE.add(UPGRADE,UPGRADE_ORDINAL),
+        VIA_BUFFER=CACHE.add(VIA,VIA_ORDINAL),
+        WARNING_BUFFER=CACHE.add(WARNING,WARNING_ORDINAL),
+        ALLOW_BUFFER=CACHE.add(ALLOW,ALLOW_ORDINAL),
+        CONTENT_ENCODING_BUFFER=CACHE.add(CONTENT_ENCODING,CONTENT_ENCODING_ORDINAL),
+        CONTENT_LANGUAGE_BUFFER=CACHE.add(CONTENT_LANGUAGE,CONTENT_LANGUAGE_ORDINAL),
+        CONTENT_LOCATION_BUFFER=CACHE.add(CONTENT_LOCATION,CONTENT_LOCATION_ORDINAL),
+        CONTENT_MD5_BUFFER=CACHE.add(CONTENT_MD5,CONTENT_MD5_ORDINAL),
+        CONTENT_RANGE_BUFFER=CACHE.add(CONTENT_RANGE,CONTENT_RANGE_ORDINAL),
+        CONTENT_TYPE_BUFFER=CACHE.add(CONTENT_TYPE,CONTENT_TYPE_ORDINAL),
+        EXPIRES_BUFFER=CACHE.add(EXPIRES,EXPIRES_ORDINAL),
+        LAST_MODIFIED_BUFFER=CACHE.add(LAST_MODIFIED,LAST_MODIFIED_ORDINAL),
+        AUTHORIZATION_BUFFER=CACHE.add(AUTHORIZATION,AUTHORIZATION_ORDINAL),
+        EXPECT_BUFFER=CACHE.add(EXPECT,EXPECT_ORDINAL),
+        FORWARDED_BUFFER=CACHE.add(FORWARDED,FORWARDED_ORDINAL),
+        FROM_BUFFER=CACHE.add(FROM,FROM_ORDINAL),
+        IF_MATCH_BUFFER=CACHE.add(IF_MATCH,IF_MATCH_ORDINAL),
+        IF_MODIFIED_SINCE_BUFFER=CACHE.add(IF_MODIFIED_SINCE,IF_MODIFIED_SINCE_ORDINAL),
+        IF_NONE_MATCH_BUFFER=CACHE.add(IF_NONE_MATCH,IF_NONE_MATCH_ORDINAL),
+        IF_RANGE_BUFFER=CACHE.add(IF_RANGE,IF_RANGE_ORDINAL),
+        IF_UNMODIFIED_SINCE_BUFFER=CACHE.add(IF_UNMODIFIED_SINCE,IF_UNMODIFIED_SINCE_ORDINAL),
+        KEEP_ALIVE_BUFFER=CACHE.add(KEEP_ALIVE,KEEP_ALIVE_ORDINAL),
+        MAX_FORWARDS_BUFFER=CACHE.add(MAX_FORWARDS,MAX_FORWARDS_ORDINAL),
+        PROXY_AUTHORIZATION_BUFFER=CACHE.add(PROXY_AUTHORIZATION,PROXY_AUTHORIZATION_ORDINAL),
+        RANGE_BUFFER=CACHE.add(RANGE,RANGE_ORDINAL),
+        REQUEST_RANGE_BUFFER=CACHE.add(REQUEST_RANGE,REQUEST_RANGE_ORDINAL),
+        REFERER_BUFFER=CACHE.add(REFERER,REFERER_ORDINAL),
+        TE_BUFFER=CACHE.add(TE,TE_ORDINAL),
+        USER_AGENT_BUFFER=CACHE.add(USER_AGENT,USER_AGENT_ORDINAL),
+        X_FORWARDED_FOR_BUFFER=CACHE.add(X_FORWARDED_FOR,X_FORWARDED_FOR_ORDINAL),
+        X_FORWARDED_PROTO_BUFFER=CACHE.add(X_FORWARDED_PROTO,X_FORWARDED_PROTO_ORDINAL),
+        X_FORWARDED_SERVER_BUFFER=CACHE.add(X_FORWARDED_SERVER,X_FORWARDED_SERVER_ORDINAL),
+        X_FORWARDED_HOST_BUFFER=CACHE.add(X_FORWARDED_HOST,X_FORWARDED_HOST_ORDINAL),
+        ACCEPT_RANGES_BUFFER=CACHE.add(ACCEPT_RANGES,ACCEPT_RANGES_ORDINAL),
+        AGE_BUFFER=CACHE.add(AGE,AGE_ORDINAL),
+        ETAG_BUFFER=CACHE.add(ETAG,ETAG_ORDINAL),
+        LOCATION_BUFFER=CACHE.add(LOCATION,LOCATION_ORDINAL),
+        PROXY_AUTHENTICATE_BUFFER=CACHE.add(PROXY_AUTHENTICATE,PROXY_AUTHENTICATE_ORDINAL),
+        RETRY_AFTER_BUFFER=CACHE.add(RETRY_AFTER,RETRY_AFTER_ORDINAL),
+        SERVER_BUFFER=CACHE.add(SERVER,SERVER_ORDINAL),
+        SERVLET_ENGINE_BUFFER=CACHE.add(SERVLET_ENGINE,SERVLET_ENGINE_ORDINAL),
+        VARY_BUFFER=CACHE.add(VARY,VARY_ORDINAL),
+        WWW_AUTHENTICATE_BUFFER=CACHE.add(WWW_AUTHENTICATE,WWW_AUTHENTICATE_ORDINAL),
+        COOKIE_BUFFER=CACHE.add(COOKIE,COOKIE_ORDINAL),
+        SET_COOKIE_BUFFER=CACHE.add(SET_COOKIE,SET_COOKIE_ORDINAL),
+        SET_COOKIE2_BUFFER=CACHE.add(SET_COOKIE2,SET_COOKIE2_ORDINAL),
+        MIME_VERSION_BUFFER=CACHE.add(MIME_VERSION,MIME_VERSION_ORDINAL),
+        IDENTITY_BUFFER=CACHE.add(IDENTITY,IDENTITY_ORDINAL),
+        PROXY_CONNECTION_BUFFER=CACHE.add(PROXY_CONNECTION,PROXY_CONNECTION_ORDINAL);
+    
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/http/HttpMethods.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,64 @@
+//
+//  ========================================================================
+//  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.http;
+
+import org.eclipse.jetty.io.Buffer;
+import org.eclipse.jetty.io.BufferCache;
+
+/* ------------------------------------------------------------------------------- */
+/** 
+ * 
+ * 
+ */
+public class HttpMethods
+{
+    public final static String GET= "GET",
+        POST= "POST",
+        HEAD= "HEAD",
+        PUT= "PUT",
+        OPTIONS= "OPTIONS",
+        DELETE= "DELETE",
+        TRACE= "TRACE",
+        CONNECT= "CONNECT",
+        MOVE= "MOVE";
+
+    public final static int GET_ORDINAL= 1,
+        POST_ORDINAL= 2,
+        HEAD_ORDINAL= 3,
+        PUT_ORDINAL= 4,
+        OPTIONS_ORDINAL= 5,
+        DELETE_ORDINAL= 6,
+        TRACE_ORDINAL= 7,
+        CONNECT_ORDINAL= 8,
+        MOVE_ORDINAL= 9;
+
+    public final static BufferCache CACHE= new BufferCache();
+
+    public final static Buffer 
+        GET_BUFFER= CACHE.add(GET, GET_ORDINAL),
+        POST_BUFFER= CACHE.add(POST, POST_ORDINAL),
+        HEAD_BUFFER= CACHE.add(HEAD, HEAD_ORDINAL),
+        PUT_BUFFER= CACHE.add(PUT, PUT_ORDINAL),
+        OPTIONS_BUFFER= CACHE.add(OPTIONS, OPTIONS_ORDINAL),
+        DELETE_BUFFER= CACHE.add(DELETE, DELETE_ORDINAL),
+        TRACE_BUFFER= CACHE.add(TRACE, TRACE_ORDINAL),
+        CONNECT_BUFFER= CACHE.add(CONNECT, CONNECT_ORDINAL),
+        MOVE_BUFFER= CACHE.add(MOVE, MOVE_ORDINAL);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/http/HttpParser.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,1279 @@
+//
+//  ========================================================================
+//  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.http;
+
+import java.io.IOException;
+
+import org.eclipse.jetty.io.Buffer;
+import org.eclipse.jetty.io.BufferCache.CachedBuffer;
+import org.eclipse.jetty.io.BufferUtil;
+import org.eclipse.jetty.io.Buffers;
+import org.eclipse.jetty.io.ByteArrayBuffer;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.EofException;
+import org.eclipse.jetty.io.View;
+import org.eclipse.jetty.io.bio.StreamEndPoint;
+import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+public class HttpParser implements Parser
+{
+    private static final Logger LOG = Log.getLogger(HttpParser.class);
+
+    // States
+    public static final int STATE_START=-14;
+    public static final int STATE_FIELD0=-13;
+    public static final int STATE_SPACE1=-12;
+    public static final int STATE_STATUS=-11;
+    public static final int STATE_URI=-10;
+    public static final int STATE_SPACE2=-9;
+    public static final int STATE_END0=-8;
+    public static final int STATE_END1=-7;
+    public static final int STATE_FIELD2=-6;
+    public static final int STATE_HEADER=-5;
+    public static final int STATE_HEADER_NAME=-4;
+    public static final int STATE_HEADER_IN_NAME=-3;
+    public static final int STATE_HEADER_VALUE=-2;
+    public static final int STATE_HEADER_IN_VALUE=-1;
+    public static final int STATE_END=0;
+    public static final int STATE_EOF_CONTENT=1;
+    public static final int STATE_CONTENT=2;
+    public static final int STATE_CHUNKED_CONTENT=3;
+    public static final int STATE_CHUNK_SIZE=4;
+    public static final int STATE_CHUNK_PARAMS=5;
+    public static final int STATE_CHUNK=6;
+    public static final int STATE_SEEKING_EOF=7;
+
+    private final EventHandler _handler;
+    private final Buffers _buffers; // source of buffers
+    private final EndPoint _endp;
+    private Buffer _header; // Buffer for header data (and small _content)
+    private Buffer _body; // Buffer for large content
+    private Buffer _buffer; // The current buffer in use (either _header or _content)
+    private CachedBuffer _cached;
+    private final View.CaseInsensitive _tok0; // Saved token: header name, request method or response version
+    private final View.CaseInsensitive _tok1; // Saved token: header value, request URI or response code
+    private String _multiLineValue;
+    private int _responseStatus; // If >0 then we are parsing a response
+    private boolean _forceContentBuffer;
+    private boolean _persistent;
+
+    /* ------------------------------------------------------------------------------- */
+    protected final View  _contentView=new View(); // View of the content in the buffer for {@link Input}
+    protected int _state=STATE_START;
+    protected byte _eol;
+    protected int _length;
+    protected long _contentLength;
+    protected long _contentPosition;
+    protected int _chunkLength;
+    protected int _chunkPosition;
+    private boolean _headResponse;
+
+    /* ------------------------------------------------------------------------------- */
+    /**
+     * Constructor.
+     */
+    public HttpParser(Buffer buffer, EventHandler handler)
+    {
+        _endp=null;
+        _buffers=null;
+        _header=buffer;
+        _buffer=buffer;
+        _handler=handler;
+
+        _tok0=new View.CaseInsensitive(_header);
+        _tok1=new View.CaseInsensitive(_header);
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    /**
+     * Constructor.
+     * @param buffers the buffers to use
+     * @param endp the endpoint
+     * @param handler the even handler
+     */
+    public HttpParser(Buffers buffers, EndPoint endp, EventHandler handler)
+    {
+        _buffers=buffers;
+        _endp=endp;
+        _handler=handler;
+        _tok0=new View.CaseInsensitive();
+        _tok1=new View.CaseInsensitive();
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    public long getContentLength()
+    {
+        return _contentLength;
+    }
+
+    /* ------------------------------------------------------------ */
+    public long getContentRead()
+    {
+        return _contentPosition;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set if a HEAD response is expected
+     * @param head
+     */
+    public void setHeadResponse(boolean head)
+    {
+        _headResponse=head;
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    public int getState()
+    {
+        return _state;
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    public boolean inContentState()
+    {
+        return _state > 0;
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    public boolean inHeaderState()
+    {
+        return _state < 0;
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    public boolean isChunking()
+    {
+        return _contentLength==HttpTokens.CHUNKED_CONTENT;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isIdle()
+    {
+        return isState(STATE_START);
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isComplete()
+    {
+        return isState(STATE_END);
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isMoreInBuffer()
+    throws IOException
+    {
+        return ( _header!=null && _header.hasContent() ||
+             _body!=null && _body.hasContent());
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    public boolean isState(int state)
+    {
+        return _state == state;
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    public boolean isPersistent()
+    {
+        return _persistent;
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    public void setPersistent(boolean persistent)
+    {
+        _persistent = persistent;
+        if (!_persistent &&(_state==STATE_END || _state==STATE_START))
+            _state=STATE_SEEKING_EOF;
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    /**
+     * Parse until {@link #STATE_END END} state.
+     * If the parser is already in the END state, then it is {@link #reset reset} and re-parsed.
+     * @throws IllegalStateException If the buffers have already been partially parsed.
+     */
+    public void parse() throws IOException
+    {
+        if (_state==STATE_END)
+            reset();
+        if (_state!=STATE_START)
+            throw new IllegalStateException("!START");
+
+        // continue parsing
+        while (_state != STATE_END)
+            if (parseNext()<0)
+                return;
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    /**
+     * Parse until END state.
+     * This method will parse any remaining content in the current buffer as long as there is
+     * no unconsumed content. It does not care about the {@link #getState current state} of the parser.
+     * @see #parse
+     * @see #parseNext
+     */
+    public boolean parseAvailable() throws IOException
+    {
+        boolean progress=parseNext()>0;
+
+        // continue parsing
+        while (!isComplete() && _buffer!=null && _buffer.length()>0 && !_contentView.hasContent())
+        {
+            progress |= parseNext()>0;
+        }
+        return progress;
+    }
+
+
+    /* ------------------------------------------------------------------------------- */
+    /**
+     * Parse until next Event.
+     * @return an indication of progress <0 EOF, 0 no progress, >0 progress.
+     */
+    public int parseNext() throws IOException
+    {
+        try
+        {
+            int progress=0;
+
+            if (_state == STATE_END)
+                return 0;
+
+            if (_buffer==null)
+                _buffer=getHeaderBuffer();
+
+
+            if (_state == STATE_CONTENT && _contentPosition == _contentLength)
+            {
+                _state=STATE_END;
+                _handler.messageComplete(_contentPosition);
+                return 1;
+            }
+
+            int length=_buffer.length();
+
+            // Fill buffer if we can
+            if (length == 0)
+            {
+                int filled=-1;
+                IOException ex=null;
+                try
+                {
+                    filled=fill();
+                    LOG.debug("filled {}/{}",filled,_buffer.length());
+                }
+                catch(IOException e)
+                {
+                    LOG.debug(this.toString(),e);
+                    ex=e;
+                }
+
+                if (filled > 0 )
+                    progress++;
+                else if (filled < 0 )
+                {
+                    _persistent=false;
+
+                    // do we have content to deliver?
+                    if (_state>STATE_END)
+                    {
+                        if (_buffer.length()>0 && !_headResponse)
+                        {
+                            Buffer chunk=_buffer.get(_buffer.length());
+                            _contentPosition += chunk.length();
+                            _contentView.update(chunk);
+                            _handler.content(chunk); // May recurse here
+                        }
+                    }
+
+                    // was this unexpected?
+                    switch(_state)
+                    {
+                        case STATE_END:
+                        case STATE_SEEKING_EOF:
+                            _state=STATE_END;
+                            break;
+
+                        case STATE_EOF_CONTENT:
+                            _state=STATE_END;
+                            _handler.messageComplete(_contentPosition);
+                            break;
+
+                        default:
+                            _state=STATE_END;
+                            if (!_headResponse)
+                                _handler.earlyEOF();
+                            _handler.messageComplete(_contentPosition);
+                    }
+
+                    if (ex!=null)
+                        throw ex;
+
+                    if (!isComplete() && !isIdle())
+                        throw new EofException();
+
+                    return -1;
+                }
+                length=_buffer.length();
+            }
+
+
+            // Handle header states
+            byte ch;
+            byte[] array=_buffer.array();
+            int last=_state;
+            while (_state<STATE_END && length-->0)
+            {
+                if (last!=_state)
+                {
+                    progress++;
+                    last=_state;
+                }
+
+                ch=_buffer.get();
+
+                if (_eol == HttpTokens.CARRIAGE_RETURN)
+                {
+                    if (ch == HttpTokens.LINE_FEED)
+                    {
+                        _eol=HttpTokens.LINE_FEED;
+                        continue;
+                    }
+                    throw new HttpException(HttpStatus.BAD_REQUEST_400);
+                }
+                _eol=0;
+
+                switch (_state)
+                {
+                    case STATE_START:
+                        _contentLength=HttpTokens.UNKNOWN_CONTENT;
+                        _cached=null;
+                        if (ch > HttpTokens.SPACE || ch<0)
+                        {
+                            _buffer.mark();
+                            _state=STATE_FIELD0;
+                        }
+                        break;
+
+                    case STATE_FIELD0:
+                        if (ch == HttpTokens.SPACE)
+                        {
+                            _tok0.update(_buffer.markIndex(), _buffer.getIndex() - 1);
+                            _responseStatus=HttpVersions.CACHE.get(_tok0)==null?-1:0;
+                            _state=STATE_SPACE1;
+                            continue;
+                        }
+                        else if (ch < HttpTokens.SPACE && ch>=0)
+                        {
+                            throw new HttpException(HttpStatus.BAD_REQUEST_400);
+                        }
+                        break;
+
+                    case STATE_SPACE1:
+                        if (ch > HttpTokens.SPACE || ch<0)
+                        {
+                            _buffer.mark();
+                            if (_responseStatus>=0)
+                            {
+                                _state=STATE_STATUS;
+                                _responseStatus=ch-'0';
+                            }
+                            else
+                                _state=STATE_URI;
+                        }
+                        else if (ch < HttpTokens.SPACE)
+                        {
+                            throw new HttpException(HttpStatus.BAD_REQUEST_400);
+                        }
+                        break;
+
+                    case STATE_STATUS:
+                        if (ch == HttpTokens.SPACE)
+                        {
+                            _tok1.update(_buffer.markIndex(), _buffer.getIndex() - 1);
+                            _state=STATE_SPACE2;
+                            continue;
+                        }
+                        else if (ch>='0' && ch<='9')
+                        {
+                            _responseStatus=_responseStatus*10+(ch-'0');
+                            continue;
+                        }
+                        else if (ch < HttpTokens.SPACE && ch>=0)
+                        {
+                            _handler.startResponse(HttpMethods.CACHE.lookup(_tok0), _responseStatus, null);
+                            _eol=ch;
+                            _state=STATE_HEADER;
+                            _tok0.setPutIndex(_tok0.getIndex());
+                            _tok1.setPutIndex(_tok1.getIndex());
+                            _multiLineValue=null;
+                            continue;
+                        }
+                        // not a digit, so must be a URI
+                        _state=STATE_URI;
+                        _responseStatus=-1;
+                        break;
+
+                    case STATE_URI:
+                        if (ch == HttpTokens.SPACE)
+                        {
+                            _tok1.update(_buffer.markIndex(), _buffer.getIndex() - 1);
+                            _state=STATE_SPACE2;
+                            continue;
+                        }
+                        else if (ch < HttpTokens.SPACE && ch>=0)
+                        {
+                            // HTTP/0.9
+                            _handler.startRequest(HttpMethods.CACHE.lookup(_tok0), _buffer.sliceFromMark(), null);
+                            _persistent=false;
+                            _state=STATE_SEEKING_EOF;
+                            _handler.headerComplete();
+                            _handler.messageComplete(_contentPosition);
+                            return 1;
+                        }
+                        break;
+
+                    case STATE_SPACE2:
+                        if (ch > HttpTokens.SPACE || ch<0)
+                        {
+                            _buffer.mark();
+                            _state=STATE_FIELD2;
+                        }
+                        else if (ch < HttpTokens.SPACE)
+                        {
+                            if (_responseStatus>0)
+                            {
+                                _handler.startResponse(HttpMethods.CACHE.lookup(_tok0), _responseStatus, null);
+                                _eol=ch;
+                                _state=STATE_HEADER;
+                                _tok0.setPutIndex(_tok0.getIndex());
+                                _tok1.setPutIndex(_tok1.getIndex());
+                                _multiLineValue=null;
+                            }
+                            else
+                            {
+                                // HTTP/0.9
+                                _handler.startRequest(HttpMethods.CACHE.lookup(_tok0), _tok1, null);
+                                _persistent=false;
+                                _state=STATE_SEEKING_EOF;
+                                _handler.headerComplete();
+                                _handler.messageComplete(_contentPosition);
+                                return 1;
+                            }
+                        }
+                        break;
+
+                    case STATE_FIELD2:
+                        if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
+                        {
+                            Buffer version;
+                            if (_responseStatus>0)
+                                _handler.startResponse(version=HttpVersions.CACHE.lookup(_tok0), _responseStatus,_buffer.sliceFromMark());
+                            else
+                                _handler.startRequest(HttpMethods.CACHE.lookup(_tok0), _tok1, version=HttpVersions.CACHE.lookup(_buffer.sliceFromMark()));
+                            _eol=ch;
+                            _persistent=HttpVersions.CACHE.getOrdinal(version)>=HttpVersions.HTTP_1_1_ORDINAL;
+                            _state=STATE_HEADER;
+                            _tok0.setPutIndex(_tok0.getIndex());
+                            _tok1.setPutIndex(_tok1.getIndex());
+                            _multiLineValue=null;
+                            continue;
+                        }
+                        break;
+
+                    case STATE_HEADER:
+                        switch(ch)
+                        {
+                            case HttpTokens.COLON:
+                            case HttpTokens.SPACE:
+                            case HttpTokens.TAB:
+                            {
+                                // header value without name - continuation?
+                                _length=-1;
+                                _state=STATE_HEADER_VALUE;
+                                break;
+                            }
+
+                            default:
+                            {
+                                // handler last header if any
+                                if (_cached!=null || _tok0.length() > 0 || _tok1.length() > 0 || _multiLineValue != null)
+                                {
+                                    Buffer header=_cached!=null?_cached:HttpHeaders.CACHE.lookup(_tok0);
+                                    _cached=null;
+                                    Buffer value=_multiLineValue == null ? _tok1 : new ByteArrayBuffer(_multiLineValue);
+
+                                    int ho=HttpHeaders.CACHE.getOrdinal(header);
+                                    if (ho >= 0)
+                                    {
+                                        int vo;
+
+                                        switch (ho)
+                                        {
+                                            case HttpHeaders.CONTENT_LENGTH_ORDINAL:
+                                                if (_contentLength != HttpTokens.CHUNKED_CONTENT )
+                                                {
+                                                    try
+                                                    {
+                                                        _contentLength=BufferUtil.toLong(value);
+                                                    }
+                                                    catch(NumberFormatException e)
+                                                    {
+                                                        LOG.ignore(e);
+                                                        throw new HttpException(HttpStatus.BAD_REQUEST_400);
+                                                    }
+                                                    if (_contentLength <= 0)
+                                                        _contentLength=HttpTokens.NO_CONTENT;
+                                                }
+                                                break;
+
+                                            case HttpHeaders.TRANSFER_ENCODING_ORDINAL:
+                                                value=HttpHeaderValues.CACHE.lookup(value);
+                                                vo=HttpHeaderValues.CACHE.getOrdinal(value);
+                                                if (HttpHeaderValues.CHUNKED_ORDINAL == vo)
+                                                    _contentLength=HttpTokens.CHUNKED_CONTENT;
+                                                else
+                                                {
+                                                    String c=value.toString(StringUtil.__ISO_8859_1);
+                                                    if (c.endsWith(HttpHeaderValues.CHUNKED))
+                                                        _contentLength=HttpTokens.CHUNKED_CONTENT;
+
+                                                    else if (c.indexOf(HttpHeaderValues.CHUNKED) >= 0)
+                                                        throw new HttpException(400,null);
+                                                }
+                                                break;
+
+                                            case HttpHeaders.CONNECTION_ORDINAL:
+                                                switch(HttpHeaderValues.CACHE.getOrdinal(value))
+                                                {
+                                                    case HttpHeaderValues.CLOSE_ORDINAL:
+                                                        _persistent=false;
+                                                        break;
+
+                                                    case HttpHeaderValues.KEEP_ALIVE_ORDINAL:
+                                                        _persistent=true;
+                                                        break;
+
+                                                    case -1: // No match, may be multi valued
+                                                    {
+                                                        for (String v : value.toString().split(","))
+                                                        {
+                                                            switch(HttpHeaderValues.CACHE.getOrdinal(v.trim()))
+                                                            {
+                                                                case HttpHeaderValues.CLOSE_ORDINAL:
+                                                                    _persistent=false;
+                                                                    break;
+
+                                                                case HttpHeaderValues.KEEP_ALIVE_ORDINAL:
+                                                                    _persistent=true;
+                                                                    break;
+                                                            }
+                                                        }
+                                                        break;
+                                                    }
+                                                }
+                                        }
+                                    }
+
+                                    _handler.parsedHeader(header, value);
+                                    _tok0.setPutIndex(_tok0.getIndex());
+                                    _tok1.setPutIndex(_tok1.getIndex());
+                                    _multiLineValue=null;
+                                }
+                                _buffer.setMarkIndex(-1);
+
+                                // now handle ch
+                                if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
+                                {
+                                    // is it a response that cannot have a body?
+                                    if (_responseStatus > 0  && // response  
+                                       (_responseStatus == 304  || // not-modified response
+                                        _responseStatus == 204 || // no-content response
+                                        _responseStatus < 200)) // 1xx response
+                                        _contentLength=HttpTokens.NO_CONTENT; // ignore any other headers set
+                                    // else if we don't know framing
+                                    else if (_contentLength == HttpTokens.UNKNOWN_CONTENT)
+                                    {
+                                        if (_responseStatus == 0  // request
+                                                || _responseStatus == 304 // not-modified response
+                                                || _responseStatus == 204 // no-content response
+                                                || _responseStatus < 200) // 1xx response
+                                            _contentLength=HttpTokens.NO_CONTENT;
+                                        else
+                                            _contentLength=HttpTokens.EOF_CONTENT;
+                                    }
+
+                                    _contentPosition=0;
+                                    _eol=ch;
+                                    if (_eol==HttpTokens.CARRIAGE_RETURN && _buffer.hasContent() && _buffer.peek()==HttpTokens.LINE_FEED)
+                                        _eol=_buffer.get();
+
+                                    // We convert _contentLength to an int for this switch statement because
+                                    // we don't care about the amount of data available just whether there is some.
+                                    switch (_contentLength > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) _contentLength)
+                                    {
+                                        case HttpTokens.EOF_CONTENT:
+                                            _state=STATE_EOF_CONTENT;
+                                            _handler.headerComplete(); // May recurse here !
+                                            break;
+
+                                        case HttpTokens.CHUNKED_CONTENT:
+                                            _state=STATE_CHUNKED_CONTENT;
+                                            _handler.headerComplete(); // May recurse here !
+                                            break;
+
+                                        case HttpTokens.NO_CONTENT:
+                                            _handler.headerComplete();
+                                            _state=_persistent||(_responseStatus>=100&&_responseStatus<200)?STATE_END:STATE_SEEKING_EOF;
+                                            _handler.messageComplete(_contentPosition);
+                                            return 1;
+
+                                        default:
+                                            _state=STATE_CONTENT;
+                                            _handler.headerComplete(); // May recurse here !
+                                            break;
+                                    }
+                                    return 1;
+                                }
+                                else
+                                {
+                                    // New header
+                                    _length=1;
+                                    _buffer.mark();
+                                    _state=STATE_HEADER_NAME;
+
+                                    // try cached name!
+                                    if (array!=null)
+                                    {
+                                        _cached=HttpHeaders.CACHE.getBest(array, _buffer.markIndex(), length+1);
+
+                                        if (_cached!=null)
+                                        {
+                                            _length=_cached.length();
+                                            _buffer.setGetIndex(_buffer.markIndex()+_length);
+                                            length=_buffer.length();
+                                        }
+                                    }
+                                }
+                            }
+                        }
+
+                        break;
+
+                    case STATE_HEADER_NAME:
+                        switch(ch)
+                        {
+                            case HttpTokens.CARRIAGE_RETURN:
+                            case HttpTokens.LINE_FEED:
+                                if (_length > 0)
+                                    _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length);
+                                _eol=ch;
+                                _state=STATE_HEADER;
+                                break;
+                            case HttpTokens.COLON:
+                                if (_length > 0 && _cached==null)
+                                    _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length);
+                                _length=-1;
+                                _state=STATE_HEADER_VALUE;
+                                break;
+                            case HttpTokens.SPACE:
+                            case HttpTokens.TAB:
+                                break;
+                            default:
+                            {
+                                _cached=null;
+                                if (_length == -1)
+                                    _buffer.mark();
+                                _length=_buffer.getIndex() - _buffer.markIndex();
+                                _state=STATE_HEADER_IN_NAME;
+                            }
+                        }
+
+                        break;
+
+                    case STATE_HEADER_IN_NAME:
+                        switch(ch)
+                        {
+                            case HttpTokens.CARRIAGE_RETURN:
+                            case HttpTokens.LINE_FEED:
+                                if (_length > 0)
+                                    _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length);
+                                _eol=ch;
+                                _state=STATE_HEADER;
+                                break;
+                            case HttpTokens.COLON:
+                                if (_length > 0 && _cached==null)
+                                    _tok0.update(_buffer.markIndex(), _buffer.markIndex() + _length);
+                                _length=-1;
+                                _state=STATE_HEADER_VALUE;
+                                break;
+                            case HttpTokens.SPACE:
+                            case HttpTokens.TAB:
+                                _state=STATE_HEADER_NAME;
+                                break;
+                            default:
+                            {
+                                _cached=null;
+                                _length++;
+                            }
+                        }
+                        break;
+
+                    case STATE_HEADER_VALUE:
+                        switch(ch)
+                        {
+                            case HttpTokens.CARRIAGE_RETURN:
+                            case HttpTokens.LINE_FEED:
+                                if (_length > 0)
+                                {
+                                    if (_tok1.length() == 0)
+                                        _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length);
+                                    else
+                                    {
+                                        // Continuation line!
+                                        if (_multiLineValue == null) _multiLineValue=_tok1.toString(StringUtil.__ISO_8859_1);
+                                        _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length);
+                                        _multiLineValue += " " + _tok1.toString(StringUtil.__ISO_8859_1);
+                                    }
+                                }
+                                _eol=ch;
+                                _state=STATE_HEADER;
+                                break;
+                            case HttpTokens.SPACE:
+                            case HttpTokens.TAB:
+                                break;
+                            default:
+                            {
+                                if (_length == -1)
+                                    _buffer.mark();
+                                _length=_buffer.getIndex() - _buffer.markIndex();
+                                _state=STATE_HEADER_IN_VALUE;
+                            }
+                        }
+                        break;
+
+                    case STATE_HEADER_IN_VALUE:
+                        switch(ch)
+                        {
+                            case HttpTokens.CARRIAGE_RETURN:
+                            case HttpTokens.LINE_FEED:
+                                if (_length > 0)
+                                {
+                                    if (_tok1.length() == 0)
+                                        _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length);
+                                    else
+                                    {
+                                        // Continuation line!
+                                        if (_multiLineValue == null) _multiLineValue=_tok1.toString(StringUtil.__ISO_8859_1);
+                                        _tok1.update(_buffer.markIndex(), _buffer.markIndex() + _length);
+                                        _multiLineValue += " " + _tok1.toString(StringUtil.__ISO_8859_1);
+                                    }
+                                }
+                                _eol=ch;
+                                _state=STATE_HEADER;
+                                break;
+                            case HttpTokens.SPACE:
+                            case HttpTokens.TAB:
+                                _state=STATE_HEADER_VALUE;
+                                break;
+                            default:
+                                _length++;
+                        }
+                        break;
+                }
+            } // end of HEADER states loop
+
+            // ==========================
+
+            // Handle HEAD response
+            if (_responseStatus>0 && _headResponse)
+            {
+                _state=_persistent||(_responseStatus>=100&&_responseStatus<200)?STATE_END:STATE_SEEKING_EOF;
+                _handler.messageComplete(_contentLength);
+            }
+
+
+            // ==========================
+
+            // Handle _content
+            length=_buffer.length();
+            Buffer chunk;
+            last=_state;
+            while (_state > STATE_END && length > 0)
+            {
+                if (last!=_state)
+                {
+                    progress++;
+                    last=_state;
+                }
+
+                if (_eol == HttpTokens.CARRIAGE_RETURN && _buffer.peek() == HttpTokens.LINE_FEED)
+                {
+                    _eol=_buffer.get();
+                    length=_buffer.length();
+                    continue;
+                }
+                _eol=0;
+                switch (_state)
+                {
+                    case STATE_EOF_CONTENT:
+                        chunk=_buffer.get(_buffer.length());
+                        _contentPosition += chunk.length();
+                        _contentView.update(chunk);
+                        _handler.content(chunk); // May recurse here
+                        // TODO adjust the _buffer to keep unconsumed content
+                        return 1;
+
+                    case STATE_CONTENT:
+                    {
+                        long remaining=_contentLength - _contentPosition;
+                        if (remaining == 0)
+                        {
+                            _state=_persistent?STATE_END:STATE_SEEKING_EOF;
+                            _handler.messageComplete(_contentPosition);
+                            return 1;
+                        }
+
+                        if (length > remaining)
+                        {
+                            // We can cast reamining to an int as we know that it is smaller than
+                            // or equal to length which is already an int.
+                            length=(int)remaining;
+                        }
+
+                        chunk=_buffer.get(length);
+                        _contentPosition += chunk.length();
+                        _contentView.update(chunk);
+                        _handler.content(chunk); // May recurse here
+
+                        if(_contentPosition == _contentLength)
+                        {
+                            _state=_persistent?STATE_END:STATE_SEEKING_EOF;
+                            _handler.messageComplete(_contentPosition);
+                        }
+                        // TODO adjust the _buffer to keep unconsumed content
+                        return 1;
+                    }
+
+                    case STATE_CHUNKED_CONTENT:
+                    {
+                        ch=_buffer.peek();
+                        if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
+                            _eol=_buffer.get();
+                        else if (ch <= HttpTokens.SPACE)
+                            _buffer.get();
+                        else
+                        {
+                            _chunkLength=0;
+                            _chunkPosition=0;
+                            _state=STATE_CHUNK_SIZE;
+                        }
+                        break;
+                    }
+
+                    case STATE_CHUNK_SIZE:
+                    {
+                        ch=_buffer.get();
+                        if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
+                        {
+                            _eol=ch;
+
+                            if (_chunkLength == 0)
+                            {
+                                if (_eol==HttpTokens.CARRIAGE_RETURN && _buffer.hasContent() && _buffer.peek()==HttpTokens.LINE_FEED)
+                                    _eol=_buffer.get();
+                                _state=_persistent?STATE_END:STATE_SEEKING_EOF;
+                                _handler.messageComplete(_contentPosition);
+                                return 1;
+                            }
+                            else
+                                _state=STATE_CHUNK;
+                        }
+                        else if (ch <= HttpTokens.SPACE || ch == HttpTokens.SEMI_COLON)
+                            _state=STATE_CHUNK_PARAMS;
+                        else if (ch >= '0' && ch <= '9')
+                            _chunkLength=_chunkLength * 16 + (ch - '0');
+                        else if (ch >= 'a' && ch <= 'f')
+                            _chunkLength=_chunkLength * 16 + (10 + ch - 'a');
+                        else if (ch >= 'A' && ch <= 'F')
+                            _chunkLength=_chunkLength * 16 + (10 + ch - 'A');
+                        else
+                            throw new IOException("bad chunk char: " + ch);
+                        break;
+                    }
+
+                    case STATE_CHUNK_PARAMS:
+                    {
+                        ch=_buffer.get();
+                        if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
+                        {
+                            _eol=ch;
+                            if (_chunkLength == 0)
+                            {
+                                if (_eol==HttpTokens.CARRIAGE_RETURN && _buffer.hasContent() && _buffer.peek()==HttpTokens.LINE_FEED)
+                                    _eol=_buffer.get();
+                                _state=_persistent?STATE_END:STATE_SEEKING_EOF;
+                                _handler.messageComplete(_contentPosition);
+                                return 1;
+                            }
+                            else
+                                _state=STATE_CHUNK;
+                        }
+                        break;
+                    }
+
+                    case STATE_CHUNK:
+                    {
+                        int remaining=_chunkLength - _chunkPosition;
+                        if (remaining == 0)
+                        {
+                            _state=STATE_CHUNKED_CONTENT;
+                            break;
+                        }
+                        else if (length > remaining)
+                            length=remaining;
+                        chunk=_buffer.get(length);
+                        _contentPosition += chunk.length();
+                        _chunkPosition += chunk.length();
+                        _contentView.update(chunk);
+                        _handler.content(chunk); // May recurse here
+                        // TODO adjust the _buffer to keep unconsumed content
+                        return 1;
+                    }
+
+                    case STATE_SEEKING_EOF:
+                    {                        
+                        // Close if there is more data than CRLF
+                        if (_buffer.length()>2)
+                        {
+                            _state=STATE_END;
+                            _endp.close();
+                        }
+                        else  
+                        {
+                            // or if the data is not white space
+                            while (_buffer.length()>0)
+                                if (!Character.isWhitespace(_buffer.get()))
+                                {
+                                    _state=STATE_END;
+                                    _endp.close();
+                                    _buffer.clear();
+                                }
+                        }
+                        
+                        _buffer.clear();
+                        break;
+                    }
+                }
+
+                length=_buffer.length();
+            }
+
+            return progress;
+        }
+        catch(HttpException e)
+        {
+            _persistent=false;
+            _state=STATE_SEEKING_EOF;
+            throw e;
+        }
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    /** fill the buffers from the endpoint
+     *
+     */
+    protected int fill() throws IOException
+    {
+        // Do we have a buffer?
+        if (_buffer==null)
+            _buffer=getHeaderBuffer();
+
+        // Is there unconsumed content in body buffer
+        if (_state>STATE_END && _buffer==_header && _header!=null && !_header.hasContent() && _body!=null && _body.hasContent())
+        {
+            _buffer=_body;
+            return _buffer.length();
+        }
+
+        // Shall we switch to a body buffer?
+        if (_buffer==_header && _state>STATE_END && _header.length()==0 && (_forceContentBuffer || (_contentLength-_contentPosition)>_header.capacity()) && (_body!=null||_buffers!=null))
+        {
+            if (_body==null)
+                _body=_buffers.getBuffer();
+            _buffer=_body;
+        }
+
+        // Do we have somewhere to fill from?
+        if (_endp != null )
+        {
+            // Shall we compact the body?
+            if (_buffer==_body || _state>STATE_END)
+            {
+                _buffer.compact();
+            }
+
+            // Are we full?
+            if (_buffer.space() == 0)
+            {
+                LOG.warn("HttpParser Full for {} ",_endp);
+                _buffer.clear();
+                throw new HttpException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413, "Request Entity Too Large: "+(_buffer==_body?"body":"head"));
+            }
+
+            try
+            {
+                int filled = _endp.fill(_buffer);
+                return filled;
+            }
+            catch(IOException e)
+            {
+                LOG.debug(e);
+                throw (e instanceof EofException) ? e:new EofException(e);
+            }
+        }
+
+        return -1;
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    public void reset()
+    {
+        // reset state
+        _contentView.setGetIndex(_contentView.putIndex());
+        _state=_persistent?STATE_START:(_endp.isInputShutdown()?STATE_END:STATE_SEEKING_EOF);
+        _contentLength=HttpTokens.UNKNOWN_CONTENT;
+        _contentPosition=0;
+        _length=0;
+        _responseStatus=0;
+
+        // Consume LF if CRLF
+        if (_eol == HttpTokens.CARRIAGE_RETURN && _buffer!=null && _buffer.hasContent() && _buffer.peek() == HttpTokens.LINE_FEED)
+            _eol=_buffer.get();
+
+        if (_body!=null && _body.hasContent())
+        {
+            // There is content in the body after the end of the request.
+            // This is probably a pipelined header of the next request, so we need to
+            // copy it to the header buffer.
+            if (_header==null)
+                getHeaderBuffer();
+            else
+            {
+                _header.setMarkIndex(-1);
+                _header.compact();
+            }
+            int take=_header.space();
+            if (take>_body.length())
+                take=_body.length();
+            _body.peek(_body.getIndex(),take);
+            _body.skip(_header.put(_body.peek(_body.getIndex(),take)));
+        }
+
+        if (_header!=null)
+        {
+            _header.setMarkIndex(-1);
+            _header.compact();
+        }
+        if (_body!=null)
+            _body.setMarkIndex(-1);
+
+        _buffer=_header;
+        returnBuffers();
+    }
+
+
+    /* ------------------------------------------------------------------------------- */
+    public void returnBuffers()
+    {
+        if (_body!=null && !_body.hasContent() && _body.markIndex()==-1 && _buffers!=null)
+        {
+            if (_buffer==_body)
+                _buffer=_header;
+            if (_buffers!=null)
+                _buffers.returnBuffer(_body);
+            _body=null;
+        }
+
+        if (_header!=null && !_header.hasContent() && _header.markIndex()==-1 && _buffers!=null)
+        {
+            if (_buffer==_header)
+                _buffer=null;
+            _buffers.returnBuffer(_header);
+            _header=null;
+        }
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    public void setState(int state)
+    {
+        this._state=state;
+        _contentLength=HttpTokens.UNKNOWN_CONTENT;
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    public String toString(Buffer buf)
+    {
+        return "state=" + _state + " length=" + _length + " buf=" + buf.hashCode();
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    @Override
+    public String toString()
+    {
+        return String.format("%s{s=%d,l=%d,c=%d}",
+                getClass().getSimpleName(),
+                _state,
+                _length,
+                _contentLength);
+    }
+
+    /* ------------------------------------------------------------ */
+    public Buffer getHeaderBuffer()
+    {
+        if (_header == null)
+        {
+            _header=_buffers.getHeader();
+            _tok0.update(_header);
+            _tok1.update(_header);
+        }
+        return _header;
+    }
+
+    /* ------------------------------------------------------------ */
+    public Buffer getBodyBuffer()
+    {
+        return _body;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param force True if a new buffer will be forced to be used for content and the header buffer will not be used.
+     */
+    public void setForceContentBuffer(boolean force)
+    {
+        _forceContentBuffer=force;
+    }
+
+    /* ------------------------------------------------------------ */
+    public Buffer blockForContent(long maxIdleTime) throws IOException
+    {
+        if (_contentView.length()>0)
+            return _contentView;
+
+        if (getState() <= STATE_END || isState(STATE_SEEKING_EOF))
+            return null;
+
+        try
+        {
+            parseNext();
+
+            // parse until some progress is made (or IOException thrown for timeout)
+            while(_contentView.length() == 0 && !(isState(HttpParser.STATE_END)||isState(HttpParser.STATE_SEEKING_EOF)) && _endp!=null && _endp.isOpen())
+            {
+                if (!_endp.isBlocking())
+                {
+                    if (parseNext()>0)
+                        continue;
+
+                    if (!_endp.blockReadable(maxIdleTime))
+                    {
+                        _endp.close();
+                        throw new EofException("timeout");
+                    }
+                }
+
+                parseNext();
+            }
+        }
+        catch(IOException e)
+        {
+            // TODO is this needed?
+            _endp.close();
+            throw e;
+        }
+
+        return _contentView.length()>0?_contentView:null;
+    }
+
+    /* ------------------------------------------------------------ */
+    /* (non-Javadoc)
+     * @see java.io.InputStream#available()
+     */
+    public int available() throws IOException
+    {
+        if (_contentView!=null && _contentView.length()>0)
+            return _contentView.length();
+
+        if (_endp.isBlocking())
+        {
+            if (_state>0 && _endp instanceof StreamEndPoint)
+                return ((StreamEndPoint)_endp).getInputStream().available()>0?1:0;
+
+            return 0;
+        }
+
+        parseNext();
+        return _contentView==null?0:_contentView.length();
+    }
+
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    public static abstract class EventHandler
+    {
+        public abstract void content(Buffer ref) throws IOException;
+
+        public void headerComplete() throws IOException
+        {
+        }
+
+        public void messageComplete(long contentLength) throws IOException
+        {
+        }
+
+        /**
+         * This is the method called by parser when a HTTP Header name and value is found
+         */
+        public void parsedHeader(Buffer name, Buffer value) throws IOException
+        {
+        }
+
+        /**
+         * This is the method called by parser when the HTTP request line is parsed
+         */
+        public abstract void startRequest(Buffer method, Buffer url, Buffer version)
+                throws IOException;
+
+        /**
+         * This is the method called by parser when the HTTP request line is parsed
+         */
+        public abstract void startResponse(Buffer version, int status, Buffer reason)
+                throws IOException;
+
+        public void earlyEOF()
+        {}
+    }
+
+
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/http/HttpSchemes.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,38 @@
+//
+//  ========================================================================
+//  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.http;
+
+import org.eclipse.jetty.io.Buffer;
+import org.eclipse.jetty.io.ByteArrayBuffer;
+
+/* ------------------------------------------------------------------------------- */
+/** 
+ * 
+ * 
+ */
+public class HttpSchemes
+{
+    public final static String
+        HTTP ="http",
+        HTTPS="https";
+    
+    public final static Buffer
+        HTTP_BUFFER = new ByteArrayBuffer(HTTP),
+        HTTPS_BUFFER = new ByteArrayBuffer(HTTPS);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/http/HttpStatus.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,1036 @@
+//
+//  ========================================================================
+//  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.http;
+
+/**
+ * <p>
+ * HttpStatusCode enum class, for status codes based on various HTTP RFCs. (see
+ * table below)
+ * </p>
+ *
+ * <table border="1" cellpadding="5">
+ * <tr>
+ * <th>Enum</th>
+ * <th>Code</th>
+ * <th>Message</th>
+ * <th>
+ * <a href="http://tools.ietf.org/html/rfc1945">RFC 1945 - HTTP/1.0</a></th>
+ * <th>
+ * <a href="http://tools.ietf.org/html/rfc2616">RFC 2616 - HTTP/1.1</a></th>
+ * <th>
+ * <a href="http://tools.ietf.org/html/rfc2518">RFC 2518 - WEBDAV</a></th>
+ * </tr>
+ *
+ * <tr>
+ * <td><strong><code>Informational - 1xx</code></strong></td>
+ * <td colspan="5">{@link #isInformational(int)}</td>
+ * </tr>
+ *
+ * <tr>
+ * <td>{@link #CONTINUE_100}</td>
+ * <td>100</td>
+ * <td>Continue</td>
+ * <td>&nbsp;</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.1.1">Sec. 10.1.1</a></td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #SWITCHING_PROTOCOLS_101}</td>
+ * <td>101</td>
+ * <td>Switching Protocols</td>
+ * <td>&nbsp;</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.1.2">Sec. 10.1.2</a></td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #PROCESSING_102}</td>
+ * <td>102</td>
+ * <td>Processing</td>
+ * <td>&nbsp;</td>
+ * <td>&nbsp;</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2518#section-10.1">Sec. 10.1</a></td>
+ * </tr>
+ *
+ * <tr>
+ * <td><strong><code>Success - 2xx</code></strong></td>
+ * <td colspan="5">{@link #isSuccess(int)}</td>
+ * </tr>
+ *
+ * <tr>
+ * <td>{@link #OK_200}</td>
+ * <td>200</td>
+ * <td>OK</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc1945#section-9.2">Sec. 9.2</a></td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.2.1">Sec. 10.2.1</a></td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #CREATED_201}</td>
+ * <td>201</td>
+ * <td>Created</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc1945#section-9.2">Sec. 9.2</a></td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.2.2">Sec. 10.2.2</a></td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #ACCEPTED_202}</td>
+ * <td>202</td>
+ * <td>Accepted</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc1945#section-9.2">Sec. 9.2</a></td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.2.3">Sec. 10.2.3</a></td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #NON_AUTHORITATIVE_INFORMATION_203}</td>
+ * <td>203</td>
+ * <td>Non Authoritative Information</td>
+ * <td>&nbsp;</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.2.4">Sec. 10.2.4</a></td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #NO_CONTENT_204}</td>
+ * <td>204</td>
+ * <td>No Content</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc1945#section-9.2">Sec. 9.2</a></td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.2.5">Sec. 10.2.5</a></td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #RESET_CONTENT_205}</td>
+ * <td>205</td>
+ * <td>Reset Content</td>
+ * <td>&nbsp;</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.2.6">Sec. 10.2.6</a></td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #PARTIAL_CONTENT_206}</td>
+ * <td>206</td>
+ * <td>Partial Content</td>
+ * <td>&nbsp;</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.2.7">Sec. 10.2.7</a></td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #MULTI_STATUS_207}</td>
+ * <td>207</td>
+ * <td>Multi-Status</td>
+ * <td>&nbsp;</td>
+ * <td>&nbsp;</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2518#section-10.2">Sec. 10.2</a></td>
+ * </tr>
+ * <tr>
+ * <td>&nbsp;</td>
+ * <td><strike>207</strike></td>
+ * <td><strike>Partial Update OK</strike></td>
+ * <td>&nbsp;</td>
+ * <td>
+ * <a href=
+ * "http://www.w3.org/Protocols/HTTP/1.1/draft-ietf-http-v11-spec-rev-01.txt"
+ * >draft/01</a></td>
+ * <td>&nbsp;</td>
+ * </tr>
+ *
+ * <tr>
+ * <td><strong><code>Redirection - 3xx</code></strong></td>
+ * <td colspan="5">{@link #isRedirection(int)}</td>
+ * </tr>
+ *
+ * <tr>
+ * <td>{@link #MULTIPLE_CHOICES_300}</td>
+ * <td>300</td>
+ * <td>Multiple Choices</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc1945#section-9.3">Sec. 9.3</a></td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.3.1">Sec. 10.3.1</a></td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #MOVED_PERMANENTLY_301}</td>
+ * <td>301</td>
+ * <td>Moved Permanently</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc1945#section-9.3">Sec. 9.3</a></td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.3.2">Sec. 10.3.2</a></td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #MOVED_TEMPORARILY_302}</td>
+ * <td>302</td>
+ * <td>Moved Temporarily</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc1945#section-9.3">Sec. 9.3</a></td>
+ * <td>(now "<code>302 Found</code>")</td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #FOUND_302}</td>
+ * <td>302</td>
+ * <td>Found</td>
+ * <td>(was "<code>302 Moved Temporarily</code>")</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.3.3">Sec. 10.3.3</a></td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #SEE_OTHER_303}</td>
+ * <td>303</td>
+ * <td>See Other</td>
+ * <td>&nbsp;</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.3.4">Sec. 10.3.4</a></td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #NOT_MODIFIED_304}</td>
+ * <td>304</td>
+ * <td>Not Modified</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc1945#section-9.3">Sec. 9.3</a></td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.3.5">Sec. 10.3.5</a></td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #USE_PROXY_305}</td>
+ * <td>305</td>
+ * <td>Use Proxy</td>
+ * <td>&nbsp;</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.3.6">Sec. 10.3.6</a></td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>&nbsp;</td>
+ * <td>306</td>
+ * <td><em>(Unused)</em></td>
+ * <td>&nbsp;</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.3.7">Sec. 10.3.7</a></td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #TEMPORARY_REDIRECT_307}</td>
+ * <td>307</td>
+ * <td>Temporary Redirect</td>
+ * <td>&nbsp;</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.3.8">Sec. 10.3.8</a></td>
+ * <td>&nbsp;</td>
+ * </tr>
+ *
+ * <tr>
+ * <td><strong><code>Client Error - 4xx</code></strong></td>
+ * <td colspan="5">{@link #isClientError(int)}</td>
+ * </tr>
+ *
+ * <tr>
+ * <td>{@link #BAD_REQUEST_400}</td>
+ * <td>400</td>
+ * <td>Bad Request</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc1945#section-9.4">Sec. 9.4</a></td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.4.1">Sec. 10.4.1</a></td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #UNAUTHORIZED_401}</td>
+ * <td>401</td>
+ * <td>Unauthorized</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc1945#section-9.4">Sec. 9.4</a></td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.4.2">Sec. 10.4.2</a></td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #PAYMENT_REQUIRED_402}</td>
+ * <td>402</td>
+ * <td>Payment Required</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc1945#section-9.4">Sec. 9.4</a></td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.4.3">Sec. 10.4.3</a></td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #FORBIDDEN_403}</td>
+ * <td>403</td>
+ * <td>Forbidden</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc1945#section-9.4">Sec. 9.4</a></td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.4.4">Sec. 10.4.4</a></td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #NOT_FOUND_404}</td>
+ * <td>404</td>
+ * <td>Not Found</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc1945#section-9.4">Sec. 9.4</a></td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.4.5">Sec. 10.4.5</a></td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #METHOD_NOT_ALLOWED_405}</td>
+ * <td>405</td>
+ * <td>Method Not Allowed</td>
+ * <td>&nbsp;</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.4.6">Sec. 10.4.6</a></td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #NOT_ACCEPTABLE_406}</td>
+ * <td>406</td>
+ * <td>Not Acceptable</td>
+ * <td>&nbsp;</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.4.7">Sec. 10.4.7</a></td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #PROXY_AUTHENTICATION_REQUIRED_407}</td>
+ * <td>407</td>
+ * <td>Proxy Authentication Required</td>
+ * <td>&nbsp;</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.4.8">Sec. 10.4.8</a></td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #REQUEST_TIMEOUT_408}</td>
+ * <td>408</td>
+ * <td>Request Timeout</td>
+ * <td>&nbsp;</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.4.9">Sec. 10.4.9</a></td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #CONFLICT_409}</td>
+ * <td>409</td>
+ * <td>Conflict</td>
+ * <td>&nbsp;</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.4.10">Sec. 10.4.10</a>
+ * </td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #GONE_410}</td>
+ * <td>410</td>
+ * <td>Gone</td>
+ * <td>&nbsp;</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.4.11">Sec. 10.4.11</a>
+ * </td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #LENGTH_REQUIRED_411}</td>
+ * <td>411</td>
+ * <td>Length Required</td>
+ * <td>&nbsp;</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.4.12">Sec. 10.4.12</a>
+ * </td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #PRECONDITION_FAILED_412}</td>
+ * <td>412</td>
+ * <td>Precondition Failed</td>
+ * <td>&nbsp;</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.4.13">Sec. 10.4.13</a>
+ * </td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #REQUEST_ENTITY_TOO_LARGE_413}</td>
+ * <td>413</td>
+ * <td>Request Entity Too Large</td>
+ * <td>&nbsp;</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.4.14">Sec. 10.4.14</a>
+ * </td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #REQUEST_URI_TOO_LONG_414}</td>
+ * <td>414</td>
+ * <td>Request-URI Too Long</td>
+ * <td>&nbsp;</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.4.15">Sec. 10.4.15</a>
+ * </td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #UNSUPPORTED_MEDIA_TYPE_415}</td>
+ * <td>415</td>
+ * <td>Unsupported Media Type</td>
+ * <td>&nbsp;</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.4.16">Sec. 10.4.16</a>
+ * </td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #REQUESTED_RANGE_NOT_SATISFIABLE_416}</td>
+ * <td>416</td>
+ * <td>Requested Range Not Satisfiable</td>
+ * <td>&nbsp;</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.4.17">Sec. 10.4.17</a>
+ * </td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #EXPECTATION_FAILED_417}</td>
+ * <td>417</td>
+ * <td>Expectation Failed</td>
+ * <td>&nbsp;</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.4.18">Sec. 10.4.18</a>
+ * </td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>&nbsp;</td>
+ * <td><strike>418</strike></td>
+ * <td><strike>Reauthentication Required</strike></td>
+ * <td>&nbsp;</td>
+ * <td>
+ * <a href=
+ * "http://tools.ietf.org/html/draft-ietf-http-v11-spec-rev-01#section-10.4.19"
+ * >draft/01</a></td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>&nbsp;</td>
+ * <td><strike>418</strike></td>
+ * <td><strike>Unprocessable Entity</strike></td>
+ * <td>&nbsp;</td>
+ * <td>&nbsp;</td>
+ * <td>
+ * <a href=
+ * "http://tools.ietf.org/html/draft-ietf-webdav-protocol-05#section-10.3"
+ * >draft/05</a></td>
+ * </tr>
+ * <tr>
+ * <td>&nbsp;</td>
+ * <td><strike>419</strike></td>
+ * <td><strike>Proxy Reauthentication Required</stike></td>
+ * <td>&nbsp;</td>
+ * <td>
+ * <a href=
+ * "http://tools.ietf.org/html/draft-ietf-http-v11-spec-rev-01#section-10.4.20"
+ * >draft/01</a></td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>&nbsp;</td>
+ * <td><strike>419</strike></td>
+ * <td><strike>Insufficient Space on Resource</stike></td>
+ * <td>&nbsp;</td>
+ * <td>&nbsp;</td>
+ * <td>
+ * <a href=
+ * "http://tools.ietf.org/html/draft-ietf-webdav-protocol-05#section-10.4"
+ * >draft/05</a></td>
+ * </tr>
+ * <tr>
+ * <td>&nbsp;</td>
+ * <td><strike>420</strike></td>
+ * <td><strike>Method Failure</strike></td>
+ * <td>&nbsp;</td>
+ * <td>&nbsp;</td>
+ * <td>
+ * <a href=
+ * "http://tools.ietf.org/html/draft-ietf-webdav-protocol-05#section-10.5"
+ * >draft/05</a></td>
+ * </tr>
+ * <tr>
+ * <td>&nbsp;</td>
+ * <td>421</td>
+ * <td><em>(Unused)</em></td>
+ * <td>&nbsp;</td>
+ * <td>&nbsp;</td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #UNPROCESSABLE_ENTITY_422}</td>
+ * <td>422</td>
+ * <td>Unprocessable Entity</td>
+ * <td>&nbsp;</td>
+ * <td>&nbsp;</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2518#section-10.3">Sec. 10.3</a></td>
+ * </tr>
+ * <tr>
+ * <td>{@link #LOCKED_423}</td>
+ * <td>423</td>
+ * <td>Locked</td>
+ * <td>&nbsp;</td>
+ * <td>&nbsp;</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2518#section-10.4">Sec. 10.4</a></td>
+ * </tr>
+ * <tr>
+ * <td>{@link #FAILED_DEPENDENCY_424}</td>
+ * <td>424</td>
+ * <td>Failed Dependency</td>
+ * <td>&nbsp;</td>
+ * <td>&nbsp;</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2518#section-10.5">Sec. 10.5</a></td>
+ * </tr>
+ *
+ * <tr>
+ * <td><strong><code>Server Error - 5xx</code></strong></td>
+ * <td colspan="5">{@link #isServerError(int)}</td>
+ * </tr>
+ *
+ * <tr>
+ * <td>{@link #INTERNAL_SERVER_ERROR_500}</td>
+ * <td>500</td>
+ * <td>Internal Server Error</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc1945#section-9.5">Sec. 9.5</a></td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.5.1">Sec. 10.5.1</a></td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #NOT_IMPLEMENTED_501}</td>
+ * <td>501</td>
+ * <td>Not Implemented</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc1945#section-9.5">Sec. 9.5</a></td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.5.2">Sec. 10.5.2</a></td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #BAD_GATEWAY_502}</td>
+ * <td>502</td>
+ * <td>Bad Gateway</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc1945#section-9.5">Sec. 9.5</a></td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.5.3">Sec. 10.5.3</a></td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #SERVICE_UNAVAILABLE_503}</td>
+ * <td>503</td>
+ * <td>Service Unavailable</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc1945#section-9.5">Sec. 9.5</a></td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.5.4">Sec. 10.5.4</a></td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #GATEWAY_TIMEOUT_504}</td>
+ * <td>504</td>
+ * <td>Gateway Timeout</td>
+ * <td>&nbsp;</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.5.5">Sec. 10.5.5</a></td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #HTTP_VERSION_NOT_SUPPORTED_505}</td>
+ * <td>505</td>
+ * <td>HTTP Version Not Supported</td>
+ * <td>&nbsp;</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2616#section-10.5.6">Sec. 10.5.6</a></td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>&nbsp;</td>
+ * <td>506</td>
+ * <td><em>(Unused)</em></td>
+ * <td>&nbsp;</td>
+ * <td>&nbsp;</td>
+ * <td>&nbsp;</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #INSUFFICIENT_STORAGE_507}</td>
+ * <td>507</td>
+ * <td>Insufficient Storage</td>
+ * <td>&nbsp;</td>
+ * <td>&nbsp;</td>
+ * <td>
+ * <a href="http://tools.ietf.org/html/rfc2518#section-10.6">Sec. 10.6</a></td>
+ * </tr>
+ *
+ * </table>
+ *
+ * @version $Id$
+ */
+public class HttpStatus
+{
+    public final static int CONTINUE_100 = 100;
+    public final static int SWITCHING_PROTOCOLS_101 = 101;
+    public final static int PROCESSING_102 = 102;
+
+    public final static int OK_200 = 200;
+    public final static int CREATED_201 = 201;
+    public final static int ACCEPTED_202 = 202;
+    public final static int NON_AUTHORITATIVE_INFORMATION_203 = 203;
+    public final static int NO_CONTENT_204 = 204;
+    public final static int RESET_CONTENT_205 = 205;
+    public final static int PARTIAL_CONTENT_206 = 206;
+    public final static int MULTI_STATUS_207 = 207;
+
+    public final static int MULTIPLE_CHOICES_300 = 300;
+    public final static int MOVED_PERMANENTLY_301 = 301;
+    public final static int MOVED_TEMPORARILY_302 = 302;
+    public final static int FOUND_302 = 302;
+    public final static int SEE_OTHER_303 = 303;
+    public final static int NOT_MODIFIED_304 = 304;
+    public final static int USE_PROXY_305 = 305;
+    public final static int TEMPORARY_REDIRECT_307 = 307;
+
+    public final static int BAD_REQUEST_400 = 400;
+    public final static int UNAUTHORIZED_401 = 401;
+    public final static int PAYMENT_REQUIRED_402 = 402;
+    public final static int FORBIDDEN_403 = 403;
+    public final static int NOT_FOUND_404 = 404;
+    public final static int METHOD_NOT_ALLOWED_405 = 405;
+    public final static int NOT_ACCEPTABLE_406 = 406;
+    public final static int PROXY_AUTHENTICATION_REQUIRED_407 = 407;
+    public final static int REQUEST_TIMEOUT_408 = 408;
+    public final static int CONFLICT_409 = 409;
+    public final static int GONE_410 = 410;
+    public final static int LENGTH_REQUIRED_411 = 411;
+    public final static int PRECONDITION_FAILED_412 = 412;
+    public final static int REQUEST_ENTITY_TOO_LARGE_413 = 413;
+    public final static int REQUEST_URI_TOO_LONG_414 = 414;
+    public final static int UNSUPPORTED_MEDIA_TYPE_415 = 415;
+    public final static int REQUESTED_RANGE_NOT_SATISFIABLE_416 = 416;
+    public final static int EXPECTATION_FAILED_417 = 417;
+    public final static int UNPROCESSABLE_ENTITY_422 = 422;
+    public final static int LOCKED_423 = 423;
+    public final static int FAILED_DEPENDENCY_424 = 424;
+
+    public final static int INTERNAL_SERVER_ERROR_500 = 500;
+    public final static int NOT_IMPLEMENTED_501 = 501;
+    public final static int BAD_GATEWAY_502 = 502;
+    public final static int SERVICE_UNAVAILABLE_503 = 503;
+    public final static int GATEWAY_TIMEOUT_504 = 504;
+    public final static int HTTP_VERSION_NOT_SUPPORTED_505 = 505;
+    public final static int INSUFFICIENT_STORAGE_507 = 507;
+
+    public static final int MAX_CODE = 507;
+
+
+    private static final Code[] codeMap = new Code[MAX_CODE+1];
+
+    static
+    {
+        for (Code code : Code.values())
+        {
+            codeMap[code._code] = code;
+        }
+    }
+
+
+    public enum Code
+    {
+        /*
+         * --------------------------------------------------------------------
+         * Informational messages in 1xx series. As defined by ... RFC 1945 -
+         * HTTP/1.0 RFC 2616 - HTTP/1.1 RFC 2518 - WebDAV
+         */
+
+        /** <code>100 Continue</code> */
+        CONTINUE(CONTINUE_100, "Continue"),
+        /** <code>101 Switching Protocols</code> */
+        SWITCHING_PROTOCOLS(SWITCHING_PROTOCOLS_101, "Switching Protocols"),
+        /** <code>102 Processing</code> */
+        PROCESSING(PROCESSING_102, "Processing"),
+
+        /*
+         * --------------------------------------------------------------------
+         * Success messages in 2xx series. As defined by ... RFC 1945 - HTTP/1.0
+         * RFC 2616 - HTTP/1.1 RFC 2518 - WebDAV
+         */
+
+        /** <code>200 OK</code> */
+        OK(OK_200, "OK"),
+        /** <code>201 Created</code> */
+        CREATED(CREATED_201, "Created"),
+        /** <code>202 Accepted</code> */
+        ACCEPTED(ACCEPTED_202, "Accepted"),
+        /** <code>203 Non Authoritative Information</code> */
+        NON_AUTHORITATIVE_INFORMATION(NON_AUTHORITATIVE_INFORMATION_203, "Non Authoritative Information"),
+        /** <code>204 No Content</code> */
+        NO_CONTENT(NO_CONTENT_204, "No Content"),
+        /** <code>205 Reset Content</code> */
+        RESET_CONTENT(RESET_CONTENT_205, "Reset Content"),
+        /** <code>206 Partial Content</code> */
+        PARTIAL_CONTENT(PARTIAL_CONTENT_206, "Partial Content"),
+        /** <code>207 Multi-Status</code> */
+        MULTI_STATUS(MULTI_STATUS_207, "Multi-Status"),
+
+        /*
+         * --------------------------------------------------------------------
+         * Redirection messages in 3xx series. As defined by ... RFC 1945 -
+         * HTTP/1.0 RFC 2616 - HTTP/1.1
+         */
+
+        /** <code>300 Mutliple Choices</code> */
+        MULTIPLE_CHOICES(MULTIPLE_CHOICES_300, "Multiple Choices"),
+        /** <code>301 Moved Permanently</code> */
+        MOVED_PERMANENTLY(MOVED_PERMANENTLY_301, "Moved Permanently"),
+        /** <code>302 Moved Temporarily</code> */
+        MOVED_TEMPORARILY(MOVED_TEMPORARILY_302, "Moved Temporarily"),
+        /** <code>302 Found</code> */
+        FOUND(FOUND_302, "Found"),
+        /** <code>303 See Other</code> */
+        SEE_OTHER(SEE_OTHER_303, "See Other"),
+        /** <code>304 Not Modified</code> */
+        NOT_MODIFIED(NOT_MODIFIED_304, "Not Modified"),
+        /** <code>305 Use Proxy</code> */
+        USE_PROXY(USE_PROXY_305, "Use Proxy"),
+        /** <code>307 Temporary Redirect</code> */
+        TEMPORARY_REDIRECT(TEMPORARY_REDIRECT_307, "Temporary Redirect"),
+
+        /*
+         * --------------------------------------------------------------------
+         * Client Error messages in 4xx series. As defined by ... RFC 1945 -
+         * HTTP/1.0 RFC 2616 - HTTP/1.1 RFC 2518 - WebDAV
+         */
+
+        /** <code>400 Bad Request</code> */
+        BAD_REQUEST(BAD_REQUEST_400, "Bad Request"),
+        /** <code>401 Unauthorized</code> */
+        UNAUTHORIZED(UNAUTHORIZED_401, "Unauthorized"),
+        /** <code>402 Payment Required</code> */
+        PAYMENT_REQUIRED(PAYMENT_REQUIRED_402, "Payment Required"),
+        /** <code>403 Forbidden</code> */
+        FORBIDDEN(FORBIDDEN_403, "Forbidden"),
+        /** <code>404 Not Found</code> */
+        NOT_FOUND(NOT_FOUND_404, "Not Found"),
+        /** <code>405 Method Not Allowed</code> */
+        METHOD_NOT_ALLOWED(METHOD_NOT_ALLOWED_405, "Method Not Allowed"),
+        /** <code>406 Not Acceptable</code> */
+        NOT_ACCEPTABLE(NOT_ACCEPTABLE_406, "Not Acceptable"),
+        /** <code>407 Proxy Authentication Required</code> */
+        PROXY_AUTHENTICATION_REQUIRED(PROXY_AUTHENTICATION_REQUIRED_407, "Proxy Authentication Required"),
+        /** <code>408 Request Timeout</code> */
+        REQUEST_TIMEOUT(REQUEST_TIMEOUT_408, "Request Timeout"),
+        /** <code>409 Conflict</code> */
+        CONFLICT(CONFLICT_409, "Conflict"),
+        /** <code>410 Gone</code> */
+        GONE(GONE_410, "Gone"),
+        /** <code>411 Length Required</code> */
+        LENGTH_REQUIRED(LENGTH_REQUIRED_411, "Length Required"),
+        /** <code>412 Precondition Failed</code> */
+        PRECONDITION_FAILED(PRECONDITION_FAILED_412, "Precondition Failed"),
+        /** <code>413 Request Entity Too Large</code> */
+        REQUEST_ENTITY_TOO_LARGE(REQUEST_ENTITY_TOO_LARGE_413, "Request Entity Too Large"),
+        /** <code>414 Request-URI Too Long</code> */
+        REQUEST_URI_TOO_LONG(REQUEST_URI_TOO_LONG_414, "Request-URI Too Long"),
+        /** <code>415 Unsupported Media Type</code> */
+        UNSUPPORTED_MEDIA_TYPE(UNSUPPORTED_MEDIA_TYPE_415, "Unsupported Media Type"),
+        /** <code>416 Requested Range Not Satisfiable</code> */
+        REQUESTED_RANGE_NOT_SATISFIABLE(REQUESTED_RANGE_NOT_SATISFIABLE_416, "Requested Range Not Satisfiable"),
+        /** <code>417 Expectation Failed</code> */
+        EXPECTATION_FAILED(EXPECTATION_FAILED_417, "Expectation Failed"),
+        /** <code>422 Unprocessable Entity</code> */
+        UNPROCESSABLE_ENTITY(UNPROCESSABLE_ENTITY_422, "Unprocessable Entity"),
+        /** <code>423 Locked</code> */
+        LOCKED(LOCKED_423, "Locked"),
+        /** <code>424 Failed Dependency</code> */
+        FAILED_DEPENDENCY(FAILED_DEPENDENCY_424, "Failed Dependency"),
+
+        /*
+         * --------------------------------------------------------------------
+         * Server Error messages in 5xx series. As defined by ... RFC 1945 -
+         * HTTP/1.0 RFC 2616 - HTTP/1.1 RFC 2518 - WebDAV
+         */
+
+        /** <code>500 Server Error</code> */
+        INTERNAL_SERVER_ERROR(INTERNAL_SERVER_ERROR_500, "Server Error"),
+        /** <code>501 Not Implemented</code> */
+        NOT_IMPLEMENTED(NOT_IMPLEMENTED_501, "Not Implemented"),
+        /** <code>502 Bad Gateway</code> */
+        BAD_GATEWAY(BAD_GATEWAY_502, "Bad Gateway"),
+        /** <code>503 Service Unavailable</code> */
+        SERVICE_UNAVAILABLE(SERVICE_UNAVAILABLE_503, "Service Unavailable"),
+        /** <code>504 Gateway Timeout</code> */
+        GATEWAY_TIMEOUT(GATEWAY_TIMEOUT_504, "Gateway Timeout"),
+        /** <code>505 HTTP Version Not Supported</code> */
+        HTTP_VERSION_NOT_SUPPORTED(HTTP_VERSION_NOT_SUPPORTED_505, "HTTP Version Not Supported"),
+        /** <code>507 Insufficient Storage</code> */
+        INSUFFICIENT_STORAGE(INSUFFICIENT_STORAGE_507, "Insufficient Storage");
+
+        private final int _code;
+        private final String _message;
+
+        private Code(int code, String message)
+        {
+            this._code = code;
+            _message=message;
+        }
+
+        public int getCode()
+        {
+            return _code;
+        }
+
+        public String getMessage()
+        {
+            return _message;
+        }
+
+
+        public boolean equals(int code)
+        {
+            return (this._code == code);
+        }
+
+        @Override
+        public String toString()
+        {
+            return String.format("[%03d %s]",this._code,this.getMessage());
+        }
+
+        /**
+         * Simple test against an code to determine if it falls into the
+         * <code>Informational</code> message category as defined in the <a
+         * href="http://tools.ietf.org/html/rfc1945">RFC 1945 - HTTP/1.0</a>,
+         * and <a href="http://tools.ietf.org/html/rfc2616">RFC 2616 -
+         * HTTP/1.1</a>.
+         *
+         * @return true if within range of codes that belongs to
+         *         <code>Informational</code> messages.
+         */
+        public boolean isInformational()
+        {
+            return HttpStatus.isInformational(this._code);
+        }
+
+        /**
+         * Simple test against an code to determine if it falls into the
+         * <code>Success</code> message category as defined in the <a
+         * href="http://tools.ietf.org/html/rfc1945">RFC 1945 - HTTP/1.0</a>,
+         * and <a href="http://tools.ietf.org/html/rfc2616">RFC 2616 -
+         * HTTP/1.1</a>.
+         *
+         * @return true if within range of codes that belongs to
+         *         <code>Success</code> messages.
+         */
+        public boolean isSuccess()
+        {
+            return HttpStatus.isSuccess(this._code);
+        }
+
+        /**
+         * Simple test against an code to determine if it falls into the
+         * <code>Redirection</code> message category as defined in the <a
+         * href="http://tools.ietf.org/html/rfc1945">RFC 1945 - HTTP/1.0</a>,
+         * and <a href="http://tools.ietf.org/html/rfc2616">RFC 2616 -
+         * HTTP/1.1</a>.
+         *
+         * @return true if within range of codes that belongs to
+         *         <code>Redirection</code> messages.
+         */
+        public boolean isRedirection()
+        {
+            return HttpStatus.isRedirection(this._code);
+        }
+
+        /**
+         * Simple test against an code to determine if it falls into the
+         * <code>Client Error</code> message category as defined in the <a
+         * href="http://tools.ietf.org/html/rfc1945">RFC 1945 - HTTP/1.0</a>,
+         * and <a href="http://tools.ietf.org/html/rfc2616">RFC 2616 -
+         * HTTP/1.1</a>.
+         *
+         * @return true if within range of codes that belongs to
+         *         <code>Client Error</code> messages.
+         */
+        public boolean isClientError()
+        {
+            return HttpStatus.isClientError(this._code);
+        }
+
+        /**
+         * Simple test against an code to determine if it falls into the
+         * <code>Server Error</code> message category as defined in the <a
+         * href="http://tools.ietf.org/html/rfc1945">RFC 1945 - HTTP/1.0</a>,
+         * and <a href="http://tools.ietf.org/html/rfc2616">RFC 2616 -
+         * HTTP/1.1</a>.
+         *
+         * @return true if within range of codes that belongs to
+         *         <code>Server Error</code> messages.
+         */
+        public boolean isServerError()
+        {
+            return HttpStatus.isServerError(this._code);
+        }
+    }
+
+
+    /**
+     * Get the HttpStatusCode for a specific code
+     *
+     * @param code
+     *            the code to lookup.
+     * @return the {@link HttpStatus} if found, or null if not found.
+     */
+    public static Code getCode(int code)
+    {
+        if (code <= MAX_CODE)
+        {
+            return codeMap[code];
+        }
+        return null;
+    }
+
+    /**
+     * Get the status message for a specific code.
+     *
+     * @param code
+     *            the code to look up
+     * @return the specific message, or the code number itself if code
+     *         does not match known list.
+     */
+    public static String getMessage(int code)
+    {
+        Code codeEnum = getCode(code);
+        if (codeEnum != null)
+        {
+            return codeEnum.getMessage();
+        }
+        else
+        {
+            return Integer.toString(code);
+        }
+    }
+
+    /**
+     * Simple test against an code to determine if it falls into the
+     * <code>Informational</code> message category as defined in the <a
+     * href="http://tools.ietf.org/html/rfc1945">RFC 1945 - HTTP/1.0</a>, and <a
+     * href="http://tools.ietf.org/html/rfc2616">RFC 2616 - HTTP/1.1</a>.
+     *
+     * @param code
+     *            the code to test.
+     * @return true if within range of codes that belongs to
+     *         <code>Informational</code> messages.
+     */
+    public static boolean isInformational(int code)
+    {
+        return ((100 <= code) && (code <= 199));
+    }
+
+    /**
+     * Simple test against an code to determine if it falls into the
+     * <code>Success</code> message category as defined in the <a
+     * href="http://tools.ietf.org/html/rfc1945">RFC 1945 - HTTP/1.0</a>, and <a
+     * href="http://tools.ietf.org/html/rfc2616">RFC 2616 - HTTP/1.1</a>.
+     *
+     * @param code
+     *            the code to test.
+     * @return true if within range of codes that belongs to
+     *         <code>Success</code> messages.
+     */
+    public static boolean isSuccess(int code)
+    {
+        return ((200 <= code) && (code <= 299));
+    }
+
+    /**
+     * Simple test against an code to determine if it falls into the
+     * <code>Redirection</code> message category as defined in the <a
+     * href="http://tools.ietf.org/html/rfc1945">RFC 1945 - HTTP/1.0</a>, and <a
+     * href="http://tools.ietf.org/html/rfc2616">RFC 2616 - HTTP/1.1</a>.
+     *
+     * @param code
+     *            the code to test.
+     * @return true if within range of codes that belongs to
+     *         <code>Redirection</code> messages.
+     */
+    public static boolean isRedirection(int code)
+    {
+        return ((300 <= code) && (code <= 399));
+    }
+
+    /**
+     * Simple test against an code to determine if it falls into the
+     * <code>Client Error</code> message category as defined in the <a
+     * href="http://tools.ietf.org/html/rfc1945">RFC 1945 - HTTP/1.0</a>, and <a
+     * href="http://tools.ietf.org/html/rfc2616">RFC 2616 - HTTP/1.1</a>.
+     *
+     * @param code
+     *            the code to test.
+     * @return true if within range of codes that belongs to
+     *         <code>Client Error</code> messages.
+     */
+    public static boolean isClientError(int code)
+    {
+        return ((400 <= code) && (code <= 499));
+    }
+
+    /**
+     * Simple test against an code to determine if it falls into the
+     * <code>Server Error</code> message category as defined in the <a
+     * href="http://tools.ietf.org/html/rfc1945">RFC 1945 - HTTP/1.0</a>, and <a
+     * href="http://tools.ietf.org/html/rfc2616">RFC 2616 - HTTP/1.1</a>.
+     *
+     * @param code
+     *            the code to test.
+     * @return true if within range of codes that belongs to
+     *         <code>Server Error</code> messages.
+     */
+    public static boolean isServerError(int code)
+    {
+        return ((500 <= code) && (code <= 599));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/http/HttpTokens.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,42 @@
+//
+//  ========================================================================
+//  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.http;
+
+/**
+ * HTTP constants
+ */
+public interface HttpTokens
+{
+    // Terminal symbols.
+    static final byte COLON= (byte)':';
+    static final byte SPACE= 0x20;
+    static final byte CARRIAGE_RETURN= 0x0D;
+    static final byte LINE_FEED= 0x0A;
+    static final byte[] CRLF = {CARRIAGE_RETURN,LINE_FEED};
+    static final byte SEMI_COLON= (byte)';';
+    static final byte TAB= 0x09;
+
+    public static final int SELF_DEFINING_CONTENT= -4;
+    public static final int UNKNOWN_CONTENT= -3;
+    public static final int CHUNKED_CONTENT= -2;
+    public static final int EOF_CONTENT= -1;
+    public static final int NO_CONTENT= 0;
+
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/http/HttpURI.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,771 @@
+//
+//  ========================================================================
+//  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.http;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+
+import org.eclipse.jetty.util.MultiMap;
+import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.TypeUtil;
+import org.eclipse.jetty.util.URIUtil;
+import org.eclipse.jetty.util.UrlEncoded;
+import org.eclipse.jetty.util.Utf8StringBuilder;
+
+
+/* ------------------------------------------------------------ */
+/** Http URI.
+ * Parse a HTTP URI from a string or byte array.  Given a URI
+ * <code>http://user@host:port/path/info;param?query#fragment</code>
+ * this class will split it into the following undecoded optional elements:<ul>
+ * <li>{@link #getScheme()} - http:</li>
+ * <li>{@link #getAuthority()} - //name@host:port</li>
+ * <li>{@link #getHost()} - host</li>
+ * <li>{@link #getPort()} - port</li>
+ * <li>{@link #getPath()} - /path/info</li>
+ * <li>{@link #getParam()} - param</li>
+ * <li>{@link #getQuery()} - query</li>
+ * <li>{@link #getFragment()} - fragment</li>
+ * </ul>
+ *
+ */
+public class HttpURI
+{
+    private static final byte[] __empty={};
+    private final static int
+    START=0,
+    AUTH_OR_PATH=1,
+    SCHEME_OR_PATH=2,
+    AUTH=4,
+    IPV6=5,
+    PORT=6,
+    PATH=7,
+    PARAM=8,
+    QUERY=9,
+    ASTERISK=10;
+
+    boolean _partial=false;
+    byte[] _raw=__empty;
+    String _rawString;
+    int _scheme;
+    int _authority;
+    int _host;
+    int _port;
+    int _portValue;
+    int _path;
+    int _param;
+    int _query;
+    int _fragment;
+    int _end;
+    boolean _encoded=false;
+
+    final Utf8StringBuilder _utf8b = new Utf8StringBuilder(64);
+
+    public HttpURI()
+    {
+
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param parsePartialAuth If True, parse auth without prior scheme, else treat all URIs starting with / as paths
+     */
+    public HttpURI(boolean parsePartialAuth)
+    {
+        _partial=parsePartialAuth;
+    }
+
+    public HttpURI(String raw)
+    {
+        _rawString=raw;
+        byte[] b;
+        try
+        {
+            b = raw.getBytes(StringUtil.__UTF8);
+        }
+        catch (UnsupportedEncodingException e)
+        {
+           throw new RuntimeException(e.getMessage());
+        }
+        parse(b,0,b.length);
+    }
+
+    public HttpURI(byte[] raw,int offset, int length)
+    {
+        parse2(raw,offset,length);
+    }
+    
+    public HttpURI(URI uri)
+    {
+        parse(uri.toASCIIString());
+    }
+
+    public void parse(String raw)
+    {
+        byte[] b = raw.getBytes();
+        parse2(b,0,b.length);
+        _rawString=raw;
+    }
+
+    public void parse(byte[] raw,int offset, int length)
+    {
+        _rawString=null;
+        parse2(raw,offset,length);
+    }
+
+
+    public void parseConnect(byte[] raw,int offset, int length)
+    {
+        _rawString=null;
+        _encoded=false;
+        _raw=raw;
+        int i=offset;
+        int e=offset+length;
+        int state=AUTH;
+        _end=offset+length;
+        _scheme=offset;
+        _authority=offset;
+        _host=offset;
+        _port=_end;
+        _portValue=-1;
+        _path=_end;
+        _param=_end;
+        _query=_end;
+        _fragment=_end;
+
+        loop: while (i<e)
+        {
+            char c=(char)(0xff&_raw[i]);
+            int s=i++;
+
+            switch (state)
+            {
+                case AUTH:
+                {
+                    switch (c)
+                    {
+                        case ':':
+                        {
+                            _port = s;
+                            break loop;
+                        }
+                        case '[':
+                        {
+                            state = IPV6;
+                            break;
+                        }
+                    }
+                    continue;
+                }
+
+                case IPV6:
+                {
+                    switch (c)
+                    {
+                        case '/':
+                        {
+                            throw new IllegalArgumentException("No closing ']' for " + StringUtil.toString(_raw,offset,length,URIUtil.__CHARSET));
+                        }
+                        case ']':
+                        {
+                            state = AUTH;
+                            break;
+                        }
+                    }
+
+                    continue;
+                }
+            }
+        }
+
+        if (_port<_path)
+            _portValue=TypeUtil.parseInt(_raw, _port+1, _path-_port-1,10);
+        else
+            throw new IllegalArgumentException("No port");
+        _path=offset;
+    }
+
+
+    private void parse2(byte[] raw,int offset, int length)
+    {
+        _encoded=false;
+        _raw=raw;
+        int i=offset;
+        int e=offset+length;
+        int state=START;
+        int m=offset;
+        _end=offset+length;
+        _scheme=offset;
+        _authority=offset;
+        _host=offset;
+        _port=offset;
+        _portValue=-1;
+        _path=offset;
+        _param=_end;
+        _query=_end;
+        _fragment=_end;
+        while (i<e)
+        {
+            char c=(char)(0xff&_raw[i]);
+            int s=i++;
+
+            state: switch (state)
+            {
+                case START:
+                {
+                    m=s;
+                    switch(c)
+                    {
+                        case '/':
+                            state=AUTH_OR_PATH;
+                            break;
+                        case ';':
+                            _param=s;
+                            state=PARAM;
+                            break;
+                        case '?':
+                            _param=s;
+                            _query=s;
+                            state=QUERY;
+                            break;
+                        case '#':
+                            _param=s;
+                            _query=s;
+                            _fragment=s;
+                            break;
+                        case '*':
+                            _path=s;
+                            state=ASTERISK;
+                            break;
+
+                        default:
+                            state=SCHEME_OR_PATH;
+                    }
+
+                    continue;
+                }
+
+                case AUTH_OR_PATH:
+                {
+                    if ((_partial||_scheme!=_authority) && c=='/')
+                    {
+                        _host=i;
+                        _port=_end;
+                        _path=_end;
+                        state=AUTH;
+                    }
+                    else if (c==';' || c=='?' || c=='#')
+                    {
+                        i--;
+                        state=PATH;
+                    }
+                    else
+                    {
+                        _host=m;
+                        _port=m;
+                        state=PATH;
+                    }
+                    continue;
+                }
+
+                case SCHEME_OR_PATH:
+                {
+                    // short cut for http and https
+                    if (length>6 && c=='t')
+                    {
+                        if (_raw[offset+3]==':')
+                        {
+                            s=offset+3;
+                            i=offset+4;
+                            c=':';
+                        }
+                        else if (_raw[offset+4]==':')
+                        {
+                            s=offset+4;
+                            i=offset+5;
+                            c=':';
+                        }
+                        else if (_raw[offset+5]==':')
+                        {
+                            s=offset+5;
+                            i=offset+6;
+                            c=':';
+                        }
+                    }
+
+                    switch (c)
+                    {
+                        case ':':
+                        {
+                            m = i++;
+                            _authority = m;
+                            _path = m;
+                            c = (char)(0xff & _raw[i]);
+                            if (c == '/')
+                                state = AUTH_OR_PATH;
+                            else
+                            {
+                                _host = m;
+                                _port = m;
+                                state = PATH;
+                            }
+                            break;
+                        }
+
+                        case '/':
+                        {
+                            state = PATH;
+                            break;
+                        }
+
+                        case ';':
+                        {
+                            _param = s;
+                            state = PARAM;
+                            break;
+                        }
+
+                        case '?':
+                        {
+                            _param = s;
+                            _query = s;
+                            state = QUERY;
+                            break;
+                        }
+
+                        case '#':
+                        {
+                            _param = s;
+                            _query = s;
+                            _fragment = s;
+                            break;
+                        }
+                    }
+                    continue;
+                }
+
+                case AUTH:
+                {
+                    switch (c)
+                    {
+
+                        case '/':
+                        {
+                            m = s;
+                            _path = m;
+                            _port = _path;
+                            state = PATH;
+                            break;
+                        }
+                        case '@':
+                        {
+                            _host = i;
+                            break;
+                        }
+                        case ':':
+                        {
+                            _port = s;
+                            state = PORT;
+                            break;
+                        }
+                        case '[':
+                        {
+                            state = IPV6;
+                            break;
+                        }
+                    }
+                    continue;
+                }
+
+                case IPV6:
+                {
+                    switch (c)
+                    {
+                        case '/':
+                        {
+                            throw new IllegalArgumentException("No closing ']' for " + StringUtil.toString(_raw,offset,length,URIUtil.__CHARSET));
+                        }
+                        case ']':
+                        {
+                            state = AUTH;
+                            break;
+                        }
+                    }
+
+                    continue;
+                }
+
+                case PORT:
+                {
+                    if (c=='/')
+                    {
+                        m=s;
+                        _path=m;
+                        if (_port<=_authority)
+                            _port=_path;
+                        state=PATH;
+                    }
+                    continue;
+                }
+
+                case PATH:
+                {
+                    switch (c)
+                    {
+                        case ';':
+                        {
+                            _param = s;
+                            state = PARAM;
+                            break;
+                        }
+                        case '?':
+                        {
+                            _param = s;
+                            _query = s;
+                            state = QUERY;
+                            break;
+                        }
+                        case '#':
+                        {
+                            _param = s;
+                            _query = s;
+                            _fragment = s;
+                            break state;
+                        }
+                        case '%':
+                        {
+                            _encoded=true;
+                        }
+                    }
+                    continue;
+                }
+
+                case PARAM:
+                {
+                    switch (c)
+                    {
+                        case '?':
+                        {
+                            _query = s;
+                            state = QUERY;
+                            break;
+                        }
+                        case '#':
+                        {
+                            _query = s;
+                            _fragment = s;
+                            break state;
+                        }
+                    }
+                    continue;
+                }
+
+                case QUERY:
+                {
+                    if (c=='#')
+                    {
+                        _fragment=s;
+                        break state;
+                    }
+                    continue;
+                }
+
+                case ASTERISK:
+                {
+                    throw new IllegalArgumentException("only '*'");
+                }
+            }
+        }
+
+        if (_port<_path)
+            _portValue=TypeUtil.parseInt(_raw, _port+1, _path-_port-1,10);
+    }
+
+    private String toUtf8String(int offset,int length)
+    {
+        _utf8b.reset();
+        _utf8b.append(_raw,offset,length);
+        return _utf8b.toString();
+    }
+
+    public String getScheme()
+    {
+        if (_scheme==_authority)
+            return null;
+        int l=_authority-_scheme;
+        if (l==5 &&
+            _raw[_scheme]=='h' &&
+            _raw[_scheme+1]=='t' &&
+            _raw[_scheme+2]=='t' &&
+            _raw[_scheme+3]=='p' )
+            return HttpSchemes.HTTP;
+        if (l==6 &&
+            _raw[_scheme]=='h' &&
+            _raw[_scheme+1]=='t' &&
+            _raw[_scheme+2]=='t' &&
+            _raw[_scheme+3]=='p' &&
+            _raw[_scheme+4]=='s' )
+            return HttpSchemes.HTTPS;
+
+        return toUtf8String(_scheme,_authority-_scheme-1);
+    }
+
+    public String getAuthority()
+    {
+        if (_authority==_path)
+            return null;
+        return toUtf8String(_authority,_path-_authority);
+    }
+
+    public String getHost()
+    {
+        if (_host==_port)
+            return null;
+        return toUtf8String(_host,_port-_host);
+    }
+
+    public int getPort()
+    {
+        return _portValue;
+    }
+
+    public String getPath()
+    {
+        if (_path==_param)
+            return null;
+        return toUtf8String(_path,_param-_path);
+    }
+
+    public String getDecodedPath()
+    {
+        if (_path==_param)
+            return null;
+
+        int length = _param-_path;
+        boolean decoding=false;
+
+        for (int i=_path;i<_param;i++)
+        {
+            byte b = _raw[i];
+
+            if (b=='%')
+            {
+                if (!decoding)
+                {
+                    _utf8b.reset();
+                    _utf8b.append(_raw,_path,i-_path);
+                    decoding=true;
+                }
+                
+                if ((i+2)>=_param)
+                    throw new IllegalArgumentException("Bad % encoding: "+this);
+                if (_raw[i+1]=='u')
+                {
+                    if ((i+5)>=_param)
+                        throw new IllegalArgumentException("Bad %u encoding: "+this);
+                    try
+                    {
+                        String unicode = new String(Character.toChars(TypeUtil.parseInt(_raw,i+2,4,16)));
+                        _utf8b.getStringBuilder().append(unicode);
+                        i+=5;
+                    }
+                    catch(Exception e)
+                    {
+                        throw new RuntimeException(e);
+                    }
+                }
+                else
+                {
+                    b=(byte)(0xff&TypeUtil.parseInt(_raw,i+1,2,16));
+                    _utf8b.append(b);
+                    i+=2;
+                }
+                continue;
+            }
+            else if (decoding)
+            {
+                _utf8b.append(b);
+            }
+        }
+
+        if (!decoding)
+            return toUtf8String(_path,length);
+        return _utf8b.toString();
+    }
+    
+    public String getDecodedPath(String encoding)
+    {
+        if (_path==_param)
+            return null;
+
+        int length = _param-_path;
+        byte[] bytes=null;
+        int n=0;
+
+        for (int i=_path;i<_param;i++)
+        {
+            byte b = _raw[i];
+
+            if (b=='%')
+            {
+                if (bytes==null)
+                {
+                    bytes=new byte[length];
+                    System.arraycopy(_raw,_path,bytes,0,n);
+                }
+                
+                if ((i+2)>=_param)
+                    throw new IllegalArgumentException("Bad % encoding: "+this);
+                if (_raw[i+1]=='u')
+                {
+                    if ((i+5)>=_param)
+                        throw new IllegalArgumentException("Bad %u encoding: "+this);
+
+                    try
+                    {
+                        String unicode = new String(Character.toChars(TypeUtil.parseInt(_raw,i+2,4,16)));
+                        byte[] encoded = unicode.getBytes(encoding);
+                        System.arraycopy(encoded,0,bytes,n,encoded.length);
+                        n+=encoded.length;
+                        i+=5;
+                    }
+                    catch(Exception e)
+                    {
+                        throw new RuntimeException(e);
+                    }
+                }
+                else
+                {
+                    b=(byte)(0xff&TypeUtil.parseInt(_raw,i+1,2,16));
+                    bytes[n++]=b;
+                    i+=2;
+                }
+                continue;
+            }
+            else if (bytes==null)
+            {
+                n++;
+                continue;
+            }
+
+            bytes[n++]=b;
+        }
+
+
+        if (bytes==null)
+            return StringUtil.toString(_raw,_path,_param-_path,encoding);
+
+        return StringUtil.toString(bytes,0,n,encoding);
+    }
+    
+    
+    
+    
+    
+
+
+    public String getPathAndParam()
+    {
+        if (_path==_query)
+            return null;
+        return toUtf8String(_path,_query-_path);
+    }
+
+    public String getCompletePath()
+    {
+        if (_path==_end)
+            return null;
+        return toUtf8String(_path,_end-_path);
+    }
+
+    public String getParam()
+    {
+        if (_param==_query)
+            return null;
+        return toUtf8String(_param+1,_query-_param-1);
+    }
+
+    public String getQuery()
+    {
+        if (_query==_fragment)
+            return null;
+        return toUtf8String(_query+1,_fragment-_query-1);
+    }
+
+    public String getQuery(String encoding)
+    {
+        if (_query==_fragment)
+            return null;
+        return StringUtil.toString(_raw,_query+1,_fragment-_query-1,encoding);
+    }
+
+    public boolean hasQuery()
+    {
+        return (_fragment>_query);
+    }
+
+    public String getFragment()
+    {
+        if (_fragment==_end)
+            return null;
+        return toUtf8String(_fragment+1,_end-_fragment-1);
+    }
+
+    public void decodeQueryTo(MultiMap parameters)
+    {
+        if (_query==_fragment)
+            return;
+        _utf8b.reset();
+        UrlEncoded.decodeUtf8To(_raw,_query+1,_fragment-_query-1,parameters,_utf8b);
+    }
+
+    public void decodeQueryTo(MultiMap parameters, String encoding)
+        throws UnsupportedEncodingException
+    {
+        if (_query==_fragment)
+            return;
+
+        if (encoding==null || StringUtil.isUTF8(encoding))
+            UrlEncoded.decodeUtf8To(_raw,_query+1,_fragment-_query-1,parameters);
+        else
+            UrlEncoded.decodeTo(StringUtil.toString(_raw,_query+1,_fragment-_query-1,encoding),parameters,encoding);
+    }
+
+    public void clear()
+    {
+        _scheme=_authority=_host=_port=_path=_param=_query=_fragment=_end=0;
+        _raw=__empty;
+        _rawString="";
+        _encoded=false;
+    }
+
+    @Override
+    public String toString()
+    {
+        if (_rawString==null)
+            _rawString=toUtf8String(_scheme,_end-_scheme);
+        return _rawString;
+    }
+
+    public void writeTo(Utf8StringBuilder buf)
+    {
+        buf.append(_raw,_scheme,_end-_scheme);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/http/HttpVersions.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,47 @@
+//
+//  ========================================================================
+//  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.http;
+
+import org.eclipse.jetty.io.Buffer;
+import org.eclipse.jetty.io.BufferCache;
+
+/* ------------------------------------------------------------------------------- */
+/** 
+ * 
+ * 
+ */
+public class HttpVersions
+{
+	public final static String
+		HTTP_0_9 = "",
+		HTTP_1_0 = "HTTP/1.0",
+		HTTP_1_1 = "HTTP/1.1";
+		
+	public final static int
+		HTTP_0_9_ORDINAL=9,
+		HTTP_1_0_ORDINAL=10,
+		HTTP_1_1_ORDINAL=11;
+	
+	public final static BufferCache CACHE = new BufferCache();
+	
+    public final static Buffer 
+        HTTP_0_9_BUFFER=CACHE.add(HTTP_0_9,HTTP_0_9_ORDINAL),
+        HTTP_1_0_BUFFER=CACHE.add(HTTP_1_0,HTTP_1_0_ORDINAL),
+        HTTP_1_1_BUFFER=CACHE.add(HTTP_1_1,HTTP_1_1_ORDINAL);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/http/MimeTypes.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,376 @@
+//
+//  ========================================================================
+//  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.http;
+
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import org.eclipse.jetty.io.Buffer;
+import org.eclipse.jetty.io.BufferCache;
+import org.eclipse.jetty.io.BufferCache.CachedBuffer;
+import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+
+/* ------------------------------------------------------------ */
+/** 
+ * 
+ */
+public class MimeTypes
+{
+    private static final Logger LOG = Log.getLogger(MimeTypes.class);
+
+    public final static String
+      FORM_ENCODED="application/x-www-form-urlencoded",
+      MESSAGE_HTTP="message/http",
+      MULTIPART_BYTERANGES="multipart/byteranges",
+      
+      TEXT_HTML="text/html",
+      TEXT_PLAIN="text/plain",
+      TEXT_XML="text/xml",
+      TEXT_JSON="text/json",
+      
+      TEXT_HTML_8859_1="text/html;charset=ISO-8859-1",
+      TEXT_PLAIN_8859_1="text/plain;charset=ISO-8859-1",
+      TEXT_XML_8859_1="text/xml;charset=ISO-8859-1",
+      
+      TEXT_HTML_UTF_8="text/html;charset=UTF-8",
+      TEXT_PLAIN_UTF_8="text/plain;charset=UTF-8",
+      TEXT_XML_UTF_8="text/xml;charset=UTF-8",
+      TEXT_JSON_UTF_8="text/json;charset=UTF-8";
+
+    private final static String
+      TEXT_HTML__8859_1="text/html; charset=ISO-8859-1",
+      TEXT_PLAIN__8859_1="text/plain; charset=ISO-8859-1",
+      TEXT_XML__8859_1="text/xml; charset=ISO-8859-1",
+      TEXT_HTML__UTF_8="text/html; charset=UTF-8",
+      TEXT_PLAIN__UTF_8="text/plain; charset=UTF-8",
+      TEXT_XML__UTF_8="text/xml; charset=UTF-8",
+      TEXT_JSON__UTF_8="text/json; charset=UTF-8";
+
+    private final static int
+	FORM_ENCODED_ORDINAL=1,
+    	MESSAGE_HTTP_ORDINAL=2,
+    	MULTIPART_BYTERANGES_ORDINAL=3,
+    	
+    	TEXT_HTML_ORDINAL=4,
+	TEXT_PLAIN_ORDINAL=5,
+	TEXT_XML_ORDINAL=6,
+        TEXT_JSON_ORDINAL=7,
+	
+        TEXT_HTML_8859_1_ORDINAL=8,
+        TEXT_PLAIN_8859_1_ORDINAL=9,
+        TEXT_XML_8859_1_ORDINAL=10,
+        
+        TEXT_HTML_UTF_8_ORDINAL=11,
+        TEXT_PLAIN_UTF_8_ORDINAL=12,
+        TEXT_XML_UTF_8_ORDINAL=13,
+        TEXT_JSON_UTF_8_ORDINAL=14;
+    
+    private static int __index=15;
+    
+    public final static BufferCache CACHE = new BufferCache(); 
+
+    public final static CachedBuffer
+    	FORM_ENCODED_BUFFER=CACHE.add(FORM_ENCODED,FORM_ENCODED_ORDINAL),
+    	MESSAGE_HTTP_BUFFER=CACHE.add(MESSAGE_HTTP, MESSAGE_HTTP_ORDINAL),
+    	MULTIPART_BYTERANGES_BUFFER=CACHE.add(MULTIPART_BYTERANGES,MULTIPART_BYTERANGES_ORDINAL),
+        
+        TEXT_HTML_BUFFER=CACHE.add(TEXT_HTML,TEXT_HTML_ORDINAL),
+        TEXT_PLAIN_BUFFER=CACHE.add(TEXT_PLAIN,TEXT_PLAIN_ORDINAL),
+        TEXT_XML_BUFFER=CACHE.add(TEXT_XML,TEXT_XML_ORDINAL),
+        TEXT_JSON_BUFFER=CACHE.add(TEXT_JSON,TEXT_JSON_ORDINAL),
+
+        TEXT_HTML_8859_1_BUFFER=CACHE.add(TEXT_HTML_8859_1,TEXT_HTML_8859_1_ORDINAL),
+        TEXT_PLAIN_8859_1_BUFFER=CACHE.add(TEXT_PLAIN_8859_1,TEXT_PLAIN_8859_1_ORDINAL),
+        TEXT_XML_8859_1_BUFFER=CACHE.add(TEXT_XML_8859_1,TEXT_XML_8859_1_ORDINAL),
+        
+        TEXT_HTML_UTF_8_BUFFER=CACHE.add(TEXT_HTML_UTF_8,TEXT_HTML_UTF_8_ORDINAL),
+        TEXT_PLAIN_UTF_8_BUFFER=CACHE.add(TEXT_PLAIN_UTF_8,TEXT_PLAIN_UTF_8_ORDINAL),
+        TEXT_XML_UTF_8_BUFFER=CACHE.add(TEXT_XML_UTF_8,TEXT_XML_UTF_8_ORDINAL),
+        TEXT_JSON_UTF_8_BUFFER=CACHE.add(TEXT_JSON_UTF_8,TEXT_JSON_UTF_8_ORDINAL),
+
+        TEXT_HTML__8859_1_BUFFER=CACHE.add(TEXT_HTML__8859_1,TEXT_HTML_8859_1_ORDINAL),
+        TEXT_PLAIN__8859_1_BUFFER=CACHE.add(TEXT_PLAIN__8859_1,TEXT_PLAIN_8859_1_ORDINAL),
+        TEXT_XML__8859_1_BUFFER=CACHE.add(TEXT_XML__8859_1,TEXT_XML_8859_1_ORDINAL),
+        
+        TEXT_HTML__UTF_8_BUFFER=CACHE.add(TEXT_HTML__UTF_8,TEXT_HTML_UTF_8_ORDINAL),
+        TEXT_PLAIN__UTF_8_BUFFER=CACHE.add(TEXT_PLAIN__UTF_8,TEXT_PLAIN_UTF_8_ORDINAL),
+        TEXT_XML__UTF_8_BUFFER=CACHE.add(TEXT_XML__UTF_8,TEXT_XML_UTF_8_ORDINAL),
+        TEXT_JSON__UTF_8_BUFFER=CACHE.add(TEXT_JSON__UTF_8,TEXT_JSON_UTF_8_ORDINAL);
+
+    
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    private final static Map __dftMimeMap = new HashMap();
+    private final static Map __encodings = new HashMap();
+    static
+    {
+        try
+        {
+            ResourceBundle mime = ResourceBundle.getBundle("org/eclipse/jetty/http/mime");
+            Enumeration i = mime.getKeys();
+            while(i.hasMoreElements())
+            {
+                String ext = (String)i.nextElement();
+                String m = mime.getString(ext);
+                __dftMimeMap.put(StringUtil.asciiToLowerCase(ext),normalizeMimeType(m));
+            }
+        }
+        catch(MissingResourceException e)
+        {
+            LOG.warn(e.toString());
+            LOG.debug(e);
+        }
+
+        try
+        {
+            ResourceBundle encoding = ResourceBundle.getBundle("org/eclipse/jetty/http/encoding");
+            Enumeration i = encoding.getKeys();
+            while(i.hasMoreElements())
+            {
+                Buffer type = normalizeMimeType((String)i.nextElement());
+                __encodings.put(type,encoding.getString(type.toString()));
+            }
+        }
+        catch(MissingResourceException e)
+        {
+            LOG.warn(e.toString());
+            LOG.debug(e);
+        }
+
+        
+        TEXT_HTML_BUFFER.setAssociate("ISO-8859-1",TEXT_HTML_8859_1_BUFFER);
+        TEXT_HTML_BUFFER.setAssociate("ISO_8859_1",TEXT_HTML_8859_1_BUFFER);
+        TEXT_HTML_BUFFER.setAssociate("iso-8859-1",TEXT_HTML_8859_1_BUFFER);
+        TEXT_PLAIN_BUFFER.setAssociate("ISO-8859-1",TEXT_PLAIN_8859_1_BUFFER);
+        TEXT_PLAIN_BUFFER.setAssociate("ISO_8859_1",TEXT_PLAIN_8859_1_BUFFER);
+        TEXT_PLAIN_BUFFER.setAssociate("iso-8859-1",TEXT_PLAIN_8859_1_BUFFER);
+        TEXT_XML_BUFFER.setAssociate("ISO-8859-1",TEXT_XML_8859_1_BUFFER);
+        TEXT_XML_BUFFER.setAssociate("ISO_8859_1",TEXT_XML_8859_1_BUFFER);
+        TEXT_XML_BUFFER.setAssociate("iso-8859-1",TEXT_XML_8859_1_BUFFER);
+
+        TEXT_HTML_BUFFER.setAssociate("UTF-8",TEXT_HTML_UTF_8_BUFFER);
+        TEXT_HTML_BUFFER.setAssociate("UTF8",TEXT_HTML_UTF_8_BUFFER);
+        TEXT_HTML_BUFFER.setAssociate("utf8",TEXT_HTML_UTF_8_BUFFER);
+        TEXT_HTML_BUFFER.setAssociate("utf-8",TEXT_HTML_UTF_8_BUFFER);
+        TEXT_PLAIN_BUFFER.setAssociate("UTF-8",TEXT_PLAIN_UTF_8_BUFFER);
+        TEXT_PLAIN_BUFFER.setAssociate("UTF8",TEXT_PLAIN_UTF_8_BUFFER);
+        TEXT_PLAIN_BUFFER.setAssociate("utf8",TEXT_PLAIN_UTF_8_BUFFER);
+        TEXT_PLAIN_BUFFER.setAssociate("utf-8",TEXT_PLAIN_UTF_8_BUFFER);
+        TEXT_XML_BUFFER.setAssociate("UTF-8",TEXT_XML_UTF_8_BUFFER);
+        TEXT_XML_BUFFER.setAssociate("UTF8",TEXT_XML_UTF_8_BUFFER);
+        TEXT_XML_BUFFER.setAssociate("utf8",TEXT_XML_UTF_8_BUFFER);
+        TEXT_XML_BUFFER.setAssociate("utf-8",TEXT_XML_UTF_8_BUFFER);
+        TEXT_JSON_BUFFER.setAssociate("UTF-8",TEXT_JSON_UTF_8_BUFFER);
+        TEXT_JSON_BUFFER.setAssociate("UTF8",TEXT_JSON_UTF_8_BUFFER);
+        TEXT_JSON_BUFFER.setAssociate("utf8",TEXT_JSON_UTF_8_BUFFER);
+        TEXT_JSON_BUFFER.setAssociate("utf-8",TEXT_JSON_UTF_8_BUFFER);
+    }
+
+
+    /* ------------------------------------------------------------ */
+    private Map _mimeMap;
+    
+    /* ------------------------------------------------------------ */
+    /** Constructor.
+     */
+    public MimeTypes()
+    {
+    }
+
+    /* ------------------------------------------------------------ */
+    public synchronized Map getMimeMap()
+    {
+        return _mimeMap;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param mimeMap A Map of file extension to mime-type.
+     */
+    public void setMimeMap(Map mimeMap)
+    {
+        if (mimeMap==null)
+        {
+            _mimeMap=null;
+            return;
+        }
+        
+        Map m=new HashMap();
+        Iterator i=mimeMap.entrySet().iterator();
+        while (i.hasNext())
+        {
+            Map.Entry entry = (Map.Entry)i.next();
+            m.put(entry.getKey(),normalizeMimeType(entry.getValue().toString()));
+        }
+        _mimeMap=m;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Get the MIME type by filename extension.
+     * @param filename A file name
+     * @return MIME type matching the longest dot extension of the
+     * file name.
+     */
+    public Buffer getMimeByExtension(String filename)
+    {
+        Buffer type=null;
+
+        if (filename!=null)
+        {
+            int i=-1;
+            while(type==null)
+            {
+                i=filename.indexOf(".",i+1);
+
+                if (i<0 || i>=filename.length())
+                    break;
+
+                String ext=StringUtil.asciiToLowerCase(filename.substring(i+1));
+                if (_mimeMap!=null)
+                    type = (Buffer)_mimeMap.get(ext);
+                if (type==null)
+                    type=(Buffer)__dftMimeMap.get(ext);
+            }
+        }
+
+        if (type==null)
+        {
+            if (_mimeMap!=null)
+                type=(Buffer)_mimeMap.get("*");
+             if (type==null)
+                 type=(Buffer)__dftMimeMap.get("*");
+        }
+
+        return type;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set a mime mapping
+     * @param extension
+     * @param type
+     */
+    public void addMimeMapping(String extension,String type)
+    {
+        if (_mimeMap==null)
+            _mimeMap=new HashMap();
+        
+        _mimeMap.put(StringUtil.asciiToLowerCase(extension),normalizeMimeType(type));
+    }
+
+    /* ------------------------------------------------------------ */
+    private static synchronized Buffer normalizeMimeType(String type)
+    {
+        Buffer b =CACHE.get(type);
+        if (b==null)
+            b=CACHE.add(type,__index++);
+        return b;
+    }
+
+    /* ------------------------------------------------------------ */
+    public static String getCharsetFromContentType(Buffer value)
+    {
+        if (value instanceof CachedBuffer)
+        {
+            switch(((CachedBuffer)value).getOrdinal())
+            {
+                case TEXT_HTML_8859_1_ORDINAL:
+                case TEXT_PLAIN_8859_1_ORDINAL:
+                case TEXT_XML_8859_1_ORDINAL:
+                    return StringUtil.__ISO_8859_1;
+
+                case TEXT_HTML_UTF_8_ORDINAL:
+                case TEXT_PLAIN_UTF_8_ORDINAL:
+                case TEXT_XML_UTF_8_ORDINAL:
+                case TEXT_JSON_UTF_8_ORDINAL:
+                    return StringUtil.__UTF8;
+            }
+        }
+        
+        int i=value.getIndex();
+        int end=value.putIndex();
+        int state=0;
+        int start=0;
+        boolean quote=false;
+        for (;i<end;i++)
+        {
+            byte b = value.peek(i);
+            
+            if (quote && state!=10)
+            {
+                if ('"'==b)
+                    quote=false;
+                continue;
+            }
+                
+            switch(state)
+            {
+                case 0:
+                    if ('"'==b)
+                    {
+                        quote=true;
+                        break;
+                    }
+                    if (';'==b)
+                        state=1;
+                    break;
+
+                case 1: if ('c'==b) state=2; else if (' '!=b) state=0; break;
+                case 2: if ('h'==b) state=3; else state=0;break;
+                case 3: if ('a'==b) state=4; else state=0;break;
+                case 4: if ('r'==b) state=5; else state=0;break;
+                case 5: if ('s'==b) state=6; else state=0;break;
+                case 6: if ('e'==b) state=7; else state=0;break;
+                case 7: if ('t'==b) state=8; else state=0;break;
+
+                case 8: if ('='==b) state=9; else if (' '!=b) state=0; break;
+                
+                case 9: 
+                    if (' '==b) 
+                        break;
+                    if ('"'==b) 
+                    {
+                        quote=true;
+                        start=i+1;
+                        state=10;
+                        break;
+                    }
+                    start=i;
+                    state=10;
+                    break;
+                    
+                case 10:
+                    if (!quote && (';'==b || ' '==b )||
+                        (quote && '"'==b ))
+                        return CACHE.lookup(value.peek(start,i-start)).toString(StringUtil.__UTF8);
+            }
+        }    
+        
+        if (state==10)
+            return CACHE.lookup(value.peek(start,i-start)).toString(StringUtil.__UTF8);
+        
+        return (String)__encodings.get(value);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/http/Parser.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,47 @@
+//
+//  ========================================================================
+//  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.http;
+
+import java.io.IOException;
+
+/**
+ * Abstract interface for a connection Parser for use by Jetty.
+ */
+public interface Parser
+{
+    void returnBuffers();
+    void reset();
+
+    boolean isComplete();
+
+    /**
+     * @return True if progress made
+     * @throws IOException
+     */
+    boolean parseAvailable() throws IOException;
+
+    boolean isMoreInBuffer() throws IOException;
+
+    boolean isIdle();
+    
+    boolean isPersistent();
+    
+    void setPersistent(boolean persistent);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/http/PathMap.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,589 @@
+//
+//  ========================================================================
+//  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.http;
+
+import java.io.Externalizable;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import org.eclipse.jetty.util.LazyList;
+import org.eclipse.jetty.util.StringMap;
+import org.eclipse.jetty.util.URIUtil;
+
+/* ------------------------------------------------------------ */
+/** URI path map to Object.
+ * This mapping implements the path specification recommended
+ * in the 2.2 Servlet API.
+ *
+ * Path specifications can be of the following forms:<PRE>
+ * /foo/bar           - an exact path specification.
+ * /foo/*             - a prefix path specification (must end '/*').
+ * *.ext              - a suffix path specification.
+ * /                  - the default path specification.
+ * </PRE>
+ * Matching is performed in the following order <NL>
+ * <LI>Exact match.
+ * <LI>Longest prefix match.
+ * <LI>Longest suffix match.
+ * <LI>default.
+ * </NL>
+ * Multiple path specifications can be mapped by providing a list of
+ * specifications. By default this class uses characters ":," as path
+ * separators, unless configured differently by calling the static
+ * method @see PathMap#setPathSpecSeparators(String)
+ * <P>
+ * Special characters within paths such as '?� and ';' are not treated specially
+ * as it is assumed they would have been either encoded in the original URL or
+ * stripped from the path.
+ * <P>
+ * This class is not synchronized.  If concurrent modifications are
+ * possible then it should be synchronized at a higher level.
+ *
+ *
+ */
+public class PathMap extends HashMap implements Externalizable
+{
+    /* ------------------------------------------------------------ */
+    private static String __pathSpecSeparators = ":,";
+
+    /* ------------------------------------------------------------ */
+    /** Set the path spec separator.
+     * Multiple path specification may be included in a single string
+     * if they are separated by the characters set in this string.
+     * By default this class uses ":," characters as path separators.
+     * @param s separators
+     */
+    public static void setPathSpecSeparators(String s)
+    {
+        __pathSpecSeparators=s;
+    }
+
+    /* --------------------------------------------------------------- */
+    final StringMap _prefixMap=new StringMap();
+    final StringMap _suffixMap=new StringMap();
+    final StringMap _exactMap=new StringMap();
+
+    List _defaultSingletonList=null;
+    Entry _prefixDefault=null;
+    Entry _default=null;
+    final Set _entrySet;
+    boolean _nodefault=false;
+
+    /* --------------------------------------------------------------- */
+    /** Construct empty PathMap.
+     */
+    public PathMap()
+    {
+        super(11);
+        _entrySet=entrySet();
+    }
+
+    /* --------------------------------------------------------------- */
+    /** Construct empty PathMap.
+     */
+    public PathMap(boolean nodefault)
+    {
+        super(11);
+        _entrySet=entrySet();
+        _nodefault=nodefault;
+    }
+
+    /* --------------------------------------------------------------- */
+    /** Construct empty PathMap.
+     */
+    public PathMap(int capacity)
+    {
+        super (capacity);
+        _entrySet=entrySet();
+    }
+
+    /* --------------------------------------------------------------- */
+    /** Construct from dictionary PathMap.
+     */
+    public PathMap(Map m)
+    {
+        putAll(m);
+        _entrySet=entrySet();
+    }
+
+    /* ------------------------------------------------------------ */
+    public void writeExternal(java.io.ObjectOutput out)
+        throws java.io.IOException
+    {
+        HashMap map = new HashMap(this);
+        out.writeObject(map);
+    }
+
+    /* ------------------------------------------------------------ */
+    public void readExternal(java.io.ObjectInput in)
+        throws java.io.IOException, ClassNotFoundException
+    {
+        HashMap map = (HashMap)in.readObject();
+        this.putAll(map);
+    }
+
+    /* --------------------------------------------------------------- */
+    /** Add a single path match to the PathMap.
+     * @param pathSpec The path specification, or comma separated list of
+     * path specifications.
+     * @param object The object the path maps to
+     */
+    @Override
+    public Object put(Object pathSpec, Object object)
+    {
+        String str = pathSpec.toString();
+        if ("".equals(str.trim()))
+        {          
+            Entry entry = new Entry("",object);
+            entry.setMapped("");
+            _exactMap.put("", entry);
+            return super.put("", object);
+        }
+        
+        StringTokenizer tok = new StringTokenizer(str,__pathSpecSeparators);
+        Object old =null;
+
+        while (tok.hasMoreTokens())
+        {
+            String spec=tok.nextToken();
+
+            if (!spec.startsWith("/") && !spec.startsWith("*."))
+                throw new IllegalArgumentException("PathSpec "+spec+". must start with '/' or '*.'");
+
+            old = super.put(spec,object);
+
+            // Make entry that was just created.
+            Entry entry = new Entry(spec,object);
+
+            if (entry.getKey().equals(spec))
+            {
+                if (spec.equals("/*"))
+                    _prefixDefault=entry;
+                else if (spec.endsWith("/*"))
+                {
+                    String mapped=spec.substring(0,spec.length()-2);
+                    entry.setMapped(mapped);
+                    _prefixMap.put(mapped,entry);
+                    _exactMap.put(mapped,entry);
+                    _exactMap.put(spec.substring(0,spec.length()-1),entry);
+                }
+                else if (spec.startsWith("*."))
+                    _suffixMap.put(spec.substring(2),entry);
+                else if (spec.equals(URIUtil.SLASH))
+                {
+                    if (_nodefault)
+                        _exactMap.put(spec,entry);
+                    else
+                    {
+                        _default=entry;
+                        _defaultSingletonList=
+                            Collections.singletonList(_default);
+                    }
+                }
+                else
+                {
+                    entry.setMapped(spec);
+                    _exactMap.put(spec,entry);
+                }
+            }
+        }
+
+        return old;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Get object matched by the path.
+     * @param path the path.
+     * @return Best matched object or null.
+     */
+    public Object match(String path)
+    {
+        Map.Entry entry = getMatch(path);
+        if (entry!=null)
+            return entry.getValue();
+        return null;
+    }
+
+
+    /* --------------------------------------------------------------- */
+    /** Get the entry mapped by the best specification.
+     * @param path the path.
+     * @return Map.Entry of the best matched  or null.
+     */
+    public Entry getMatch(String path)
+    {
+        Map.Entry entry=null;
+
+        if (path==null)
+            return null;
+
+        int l=path.length();
+        
+        //special case
+        if (l == 1 && path.charAt(0)=='/')
+        {
+            entry = (Map.Entry)_exactMap.get("");
+            if (entry != null)
+                return (Entry)entry;
+        }
+        
+        // try exact match
+        entry=_exactMap.getEntry(path,0,l);
+        if (entry!=null)
+            return (Entry) entry.getValue();
+
+        // prefix search
+        int i=l;
+        while((i=path.lastIndexOf('/',i-1))>=0)
+        {
+            entry=_prefixMap.getEntry(path,0,i);
+            if (entry!=null)
+                return (Entry) entry.getValue();
+        }
+
+        // Prefix Default
+        if (_prefixDefault!=null)
+            return _prefixDefault;
+
+        // Extension search
+        i=0;
+        while ((i=path.indexOf('.',i+1))>0)
+        {
+            entry=_suffixMap.getEntry(path,i+1,l-i-1);
+            if (entry!=null)
+                return (Entry) entry.getValue();
+        }
+
+        // Default
+        return _default;
+    }
+
+    /* --------------------------------------------------------------- */
+    /** Get all entries matched by the path.
+     * Best match first.
+     * @param path Path to match
+     * @return LazyList of Map.Entry instances key=pathSpec
+     */
+    public Object getLazyMatches(String path)
+    {
+        Map.Entry entry;
+        Object entries=null;
+
+        if (path==null)
+            return LazyList.getList(entries);
+
+        int l=path.length();
+
+        // try exact match
+        entry=_exactMap.getEntry(path,0,l);
+        if (entry!=null)
+            entries=LazyList.add(entries,entry.getValue());
+
+        // prefix search
+        int i=l-1;
+        while((i=path.lastIndexOf('/',i-1))>=0)
+        {
+            entry=_prefixMap.getEntry(path,0,i);
+            if (entry!=null)
+                entries=LazyList.add(entries,entry.getValue());
+        }
+
+        // Prefix Default
+        if (_prefixDefault!=null)
+            entries=LazyList.add(entries,_prefixDefault);
+
+        // Extension search
+        i=0;
+        while ((i=path.indexOf('.',i+1))>0)
+        {
+            entry=_suffixMap.getEntry(path,i+1,l-i-1);
+            if (entry!=null)
+                entries=LazyList.add(entries,entry.getValue());
+        }
+
+        // Default
+        if (_default!=null)
+        {
+            // Optimization for just the default
+            if (entries==null)
+                return _defaultSingletonList;
+
+            entries=LazyList.add(entries,_default);
+        }
+
+        return entries;
+    }
+
+    /* --------------------------------------------------------------- */
+    /** Get all entries matched by the path.
+     * Best match first.
+     * @param path Path to match
+     * @return List of Map.Entry instances key=pathSpec
+     */
+    public List getMatches(String path)
+    {
+        return LazyList.getList(getLazyMatches(path));
+    }
+
+    /* --------------------------------------------------------------- */
+    /** Return whether the path matches any entries in the PathMap,
+     * excluding the default entry
+     * @param path Path to match
+     * @return Whether the PathMap contains any entries that match this
+     */
+    public boolean containsMatch(String path)
+    {
+    	Entry match = getMatch(path);
+    	return match!=null && !match.equals(_default);
+    }
+
+    /* --------------------------------------------------------------- */
+    @Override
+    public Object remove(Object pathSpec)
+    {
+        if (pathSpec!=null)
+        {
+            String spec=(String) pathSpec;
+            if (spec.equals("/*"))
+                _prefixDefault=null;
+            else if (spec.endsWith("/*"))
+            {
+                _prefixMap.remove(spec.substring(0,spec.length()-2));
+                _exactMap.remove(spec.substring(0,spec.length()-1));
+                _exactMap.remove(spec.substring(0,spec.length()-2));
+            }
+            else if (spec.startsWith("*."))
+                _suffixMap.remove(spec.substring(2));
+            else if (spec.equals(URIUtil.SLASH))
+            {
+                _default=null;
+                _defaultSingletonList=null;
+            }
+            else
+                _exactMap.remove(spec);
+        }
+        return super.remove(pathSpec);
+    }
+
+    /* --------------------------------------------------------------- */
+    @Override
+    public void clear()
+    {
+        _exactMap.clear();
+        _prefixMap.clear();
+        _suffixMap.clear();
+        _default=null;
+        _defaultSingletonList=null;
+        super.clear();
+    }
+
+    /* --------------------------------------------------------------- */
+    /**
+     * @return true if match.
+     */
+    public static boolean match(String pathSpec, String path)
+        throws IllegalArgumentException
+    {
+        return match(pathSpec, path, false);
+    }
+
+    /* --------------------------------------------------------------- */
+    /**
+     * @return true if match.
+     */
+    public static boolean match(String pathSpec, String path, boolean noDefault)
+    throws IllegalArgumentException
+    {
+        if (pathSpec.length()==0)
+            return "/".equals(path);
+            
+        char c = pathSpec.charAt(0);
+        if (c=='/')
+        {
+            if (!noDefault && pathSpec.length()==1 || pathSpec.equals(path))
+                return true;
+
+            if(isPathWildcardMatch(pathSpec, path))
+                return true;
+        }
+        else if (c=='*')
+            return path.regionMatches(path.length()-pathSpec.length()+1,
+                                      pathSpec,1,pathSpec.length()-1);
+        return false;
+    }
+
+    /* --------------------------------------------------------------- */
+    private static boolean isPathWildcardMatch(String pathSpec, String path)
+    {
+        // For a spec of "/foo/*" match "/foo" , "/foo/..." but not "/foobar"
+        int cpl=pathSpec.length()-2;
+        if (pathSpec.endsWith("/*") && path.regionMatches(0,pathSpec,0,cpl))
+        {
+            if (path.length()==cpl || '/'==path.charAt(cpl))
+                return true;
+        }
+        return false;
+    }
+
+
+    /* --------------------------------------------------------------- */
+    /** Return the portion of a path that matches a path spec.
+     * @return null if no match at all.
+     */
+    public static String pathMatch(String pathSpec, String path)
+    {
+        char c = pathSpec.charAt(0);
+
+        if (c=='/')
+        {
+            if (pathSpec.length()==1)
+                return path;
+
+            if (pathSpec.equals(path))
+                return path;
+
+            if (isPathWildcardMatch(pathSpec, path))
+                return path.substring(0,pathSpec.length()-2);
+        }
+        else if (c=='*')
+        {
+            if (path.regionMatches(path.length()-(pathSpec.length()-1),
+                                   pathSpec,1,pathSpec.length()-1))
+                return path;
+        }
+        return null;
+    }
+
+    /* --------------------------------------------------------------- */
+    /** Return the portion of a path that is after a path spec.
+     * @return The path info string
+     */
+    public static String pathInfo(String pathSpec, String path)
+    {
+        if ("".equals(pathSpec))
+            return path; //servlet 3 spec sec 12.2 will be '/'
+        
+        char c = pathSpec.charAt(0);
+
+        if (c=='/')
+        {
+            if (pathSpec.length()==1)
+                return null;
+
+            boolean wildcard = isPathWildcardMatch(pathSpec, path);
+
+            // handle the case where pathSpec uses a wildcard and path info is "/*"
+            if (pathSpec.equals(path) && !wildcard)
+                return null;
+
+            if (wildcard)
+            {
+                if (path.length()==pathSpec.length()-2)
+                    return null;
+                return path.substring(pathSpec.length()-2);
+            }
+        }
+        return null;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /** Relative path.
+     * @param base The base the path is relative to.
+     * @param pathSpec The spec of the path segment to ignore.
+     * @param path the additional path
+     * @return base plus path with pathspec removed
+     */
+    public static String relativePath(String base,
+                                      String pathSpec,
+                                      String path )
+    {
+        String info=pathInfo(pathSpec,path);
+        if (info==null)
+            info=path;
+
+        if( info.startsWith( "./"))
+            info = info.substring( 2);
+        if( base.endsWith( URIUtil.SLASH))
+            if( info.startsWith( URIUtil.SLASH))
+                path = base + info.substring(1);
+            else
+                path = base + info;
+        else
+            if( info.startsWith( URIUtil.SLASH))
+                path = base + info;
+            else
+                path = base + URIUtil.SLASH + info;
+        return path;
+    }
+
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    public static class Entry implements Map.Entry
+    {
+        private final Object key;
+        private final Object value;
+        private String mapped;
+        private transient String string;
+
+        Entry(Object key, Object value)
+        {
+            this.key=key;
+            this.value=value;
+        }
+
+        public Object getKey()
+        {
+            return key;
+        }
+
+        public Object getValue()
+        {
+            return value;
+        }
+
+        public Object setValue(Object o)
+        {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public String toString()
+        {
+            if (string==null)
+                string=key+"="+value;
+            return string;
+        }
+
+        public String getMapped()
+        {
+            return mapped;
+        }
+
+        void setMapped(String mapped)
+        {
+            this.mapped = mapped;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/http/encoding.properties	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,4 @@
+text/html	= ISO-8859-1
+text/plain	= ISO-8859-1
+text/xml	= UTF-8
+text/json   = UTF-8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/http/gzip/AbstractCompressedStream.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,388 @@
+//
+//  ========================================================================
+//  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.http.gzip;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.zip.DeflaterOutputStream;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.util.ByteArrayOutputStream2;
+
+/* ------------------------------------------------------------ */
+/**
+ * Skeletal implementation of a CompressedStream. This class adds compression features to a ServletOutputStream and takes care of setting response headers, etc.
+ * Major work and configuration is done here. Subclasses using different kinds of compression only have to implement the abstract methods doCompress() and
+ * setContentEncoding() using the desired compression and setting the appropriate Content-Encoding header string.
+ */
+public abstract class AbstractCompressedStream extends ServletOutputStream 
+{
+    private final String _encoding;
+    protected final String _vary;
+    protected final CompressedResponseWrapper _wrapper;
+    protected final HttpServletResponse _response;
+    protected OutputStream _out;
+    protected ByteArrayOutputStream2 _bOut;
+    protected DeflaterOutputStream _compressedOutputStream;
+    protected boolean _closed;
+    protected boolean _doNotCompress;
+
+    /**
+     * Instantiates a new compressed stream.
+     * 
+     */
+    public AbstractCompressedStream(String encoding,HttpServletRequest request, CompressedResponseWrapper wrapper,String vary)
+            throws IOException
+    {
+        _encoding=encoding;
+        _wrapper = wrapper;
+        _response = (HttpServletResponse)wrapper.getResponse();
+        _vary=vary;
+        
+        if (_wrapper.getMinCompressSize()==0)
+            doCompress();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Reset buffer.
+     */
+    public void resetBuffer()
+    {
+        if (_response.isCommitted() || _compressedOutputStream!=null )
+            throw new IllegalStateException("Committed");
+        _closed = false;
+        _out = null;
+        _bOut = null;
+        _doNotCompress = false;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setBufferSize(int bufferSize)
+    {
+        if (_bOut!=null && _bOut.getBuf().length<bufferSize)
+        {
+            ByteArrayOutputStream2 b = new ByteArrayOutputStream2(bufferSize);
+            b.write(_bOut.getBuf(),0,_bOut.size());
+            _bOut=b;
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    public void setContentLength()
+    {
+        if (_doNotCompress)
+        {
+            long length=_wrapper.getContentLength();
+            if (length>=0)
+            {
+                if (length < Integer.MAX_VALUE)
+                    _response.setContentLength((int)length);
+                else
+                    _response.setHeader("Content-Length",Long.toString(length));
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see java.io.OutputStream#flush()
+     */
+    @Override
+    public void flush() throws IOException
+    {
+        if (_out == null || _bOut != null)
+        {
+            long length=_wrapper.getContentLength();
+            if (length > 0 && length < _wrapper.getMinCompressSize())
+                doNotCompress(false);
+            else
+                doCompress();
+        }
+
+        _out.flush();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see java.io.OutputStream#close()
+     */
+    @Override
+    public void close() throws IOException
+    {
+        if (_closed)
+            return;
+
+        if (_wrapper.getRequest().getAttribute("javax.servlet.include.request_uri") != null)
+            flush();
+        else
+        {
+            if (_bOut != null)
+            {
+                long length=_wrapper.getContentLength();
+                if (length < 0)
+                {
+                    length = _bOut.getCount();
+                    _wrapper.setContentLength(length);
+                }
+                if (length < _wrapper.getMinCompressSize())
+                    doNotCompress(false);
+                else
+                    doCompress();
+            }
+            else if (_out == null)
+            {
+                // No output
+                doNotCompress(false);
+            }
+
+            if (_compressedOutputStream != null)
+                _compressedOutputStream.close();
+            else
+                _out.close();
+            _closed = true;
+        }
+    }
+
+    /**
+     * Finish.
+     * 
+     * @throws IOException
+     *             Signals that an I/O exception has occurred.
+     */
+    public void finish() throws IOException
+    {
+        if (!_closed)
+        {
+            if (_out == null || _bOut != null)
+            {
+                long length=_wrapper.getContentLength();
+                if (length >= 0 && length < _wrapper.getMinCompressSize())
+                    doNotCompress(false);
+                else
+                    doCompress();
+            }
+
+            if (_compressedOutputStream != null && !_closed)
+            {
+                _closed = true;
+                _compressedOutputStream.close();
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see java.io.OutputStream#write(int)
+     */
+    @Override
+    public void write(int b) throws IOException
+    {
+        checkOut(1);
+        _out.write(b);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see java.io.OutputStream#write(byte[])
+     */
+    @Override
+    public void write(byte b[]) throws IOException
+    {
+        checkOut(b.length);
+        _out.write(b);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see java.io.OutputStream#write(byte[], int, int)
+     */
+    @Override
+    public void write(byte b[], int off, int len) throws IOException
+    {
+        checkOut(len);
+        _out.write(b,off,len);
+    }
+    
+    /**
+     * Do compress.
+     *
+     * @throws IOException Signals that an I/O exception has occurred.
+     */
+    public void doCompress() throws IOException
+    {
+        if (_compressedOutputStream==null) 
+        {
+            if (_response.isCommitted())
+                throw new IllegalStateException();
+            
+            if (_encoding!=null)
+            {
+                setHeader("Content-Encoding", _encoding);            
+                if (_response.containsHeader("Content-Encoding"))
+                {
+                    addHeader("Vary",_vary);
+                    _out=_compressedOutputStream=createStream();
+                    if (_out!=null)
+                    {
+                        if (_bOut!=null)
+                        {
+                            _out.write(_bOut.getBuf(),0,_bOut.getCount());
+                            _bOut=null;
+                        }
+
+                        String etag=_wrapper.getETag();
+                        if (etag!=null)
+                            setHeader("ETag",etag.substring(0,etag.length()-1)+'-'+_encoding+'"');
+                        return;
+                    }
+                }
+            }
+            
+            doNotCompress(true); // Send vary as it could have been compressed if encoding was present
+        }
+    }
+
+    /**
+     * Do not compress.
+     * 
+     * @throws IOException
+     *             Signals that an I/O exception has occurred.
+     */
+    public void doNotCompress(boolean sendVary) throws IOException
+    {
+        if (_compressedOutputStream != null)
+            throw new IllegalStateException("Compressed output stream is already assigned.");
+        if (_out == null || _bOut != null)
+        {
+            if (sendVary)
+                addHeader("Vary",_vary);
+            if (_wrapper.getETag()!=null)
+                setHeader("ETag",_wrapper.getETag());
+                
+            _doNotCompress = true;
+
+            _out = _response.getOutputStream();
+            setContentLength();
+
+            if (_bOut != null)
+                _out.write(_bOut.getBuf(),0,_bOut.getCount());
+            _bOut = null;
+        }
+    }
+
+    /**
+     * Check out.
+     * 
+     * @param lengthToWrite
+     *            the length
+     * @throws IOException
+     *             Signals that an I/O exception has occurred.
+     */
+    private void checkOut(int lengthToWrite) throws IOException
+    {
+        if (_closed)
+            throw new IOException("CLOSED");
+
+        if (_out == null)
+        {            
+            // If this first write is larger than buffer size, then we are committing now
+            if (lengthToWrite>_wrapper.getBufferSize())
+            {
+                // if we know this is all the content and it is less than minimum, then do not compress, otherwise do compress
+                long length=_wrapper.getContentLength();
+                if (length>=0 && length<_wrapper.getMinCompressSize())
+                    doNotCompress(false);  // Not compressing by size, so no vary on request headers
+                else
+                    doCompress();
+            }
+            else
+            {
+                // start aggregating writes into a buffered output stream
+                _out = _bOut = new ByteArrayOutputStream2(_wrapper.getBufferSize());
+            }
+        }
+        // else are we aggregating writes?
+        else if (_bOut !=null)
+        {
+            // We are aggregating into the buffered output stream.  
+
+            // If this write fills the buffer, then we are committing
+            if (lengthToWrite>=(_bOut.getBuf().length - _bOut.getCount()))
+            {
+                // if we know this is all the content and it is less than minimum, then do not compress, otherwise do compress
+                long length=_wrapper.getContentLength();
+                if (length>=0 && length<_wrapper.getMinCompressSize())
+                    doNotCompress(false);  // Not compressing by size, so no vary on request headers
+                else
+                    doCompress();
+            }
+        }
+    }
+
+    /**
+     * @see org.eclipse.jetty.http.gzip.CompressedStream#getOutputStream()
+     */
+    public OutputStream getOutputStream()
+    {
+        return _out;
+    }
+
+    /**
+     * @see org.eclipse.jetty.http.gzip.CompressedStream#isClosed()
+     */
+    public boolean isClosed()
+    {
+        return _closed;
+    }
+    
+    /**
+     * Allows derived implementations to replace PrintWriter implementation.
+     */
+    protected PrintWriter newWriter(OutputStream out, String encoding) throws UnsupportedEncodingException
+    {
+        return encoding == null?new PrintWriter(out):new PrintWriter(new OutputStreamWriter(out,encoding));
+    }
+
+    protected void addHeader(String name,String value)
+    {
+        _response.addHeader(name, value);
+    }
+
+    protected void setHeader(String name,String value)
+    {
+        _response.setHeader(name, value);
+    }
+    
+    /**
+     * Create the stream fitting to the underlying compression type.
+     * 
+     * @throws IOException
+     *             Signals that an I/O exception has occurred.
+     */
+    protected abstract DeflaterOutputStream createStream() throws IOException;
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/http/gzip/CompressedResponseWrapper.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,487 @@
+//
+//  ========================================================================
+//  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.http.gzip;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.Set;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+import org.eclipse.jetty.util.StringUtil;
+
+/*------------------------------------------------------------ */
+/**
+ */
+public abstract class CompressedResponseWrapper extends HttpServletResponseWrapper
+{
+    
+    public static final int DEFAULT_BUFFER_SIZE = 8192;
+    public static final int DEFAULT_MIN_COMPRESS_SIZE = 256;
+    
+    private Set<String> _mimeTypes;
+    private int _bufferSize=DEFAULT_BUFFER_SIZE;
+    private int _minCompressSize=DEFAULT_MIN_COMPRESS_SIZE;
+    protected HttpServletRequest _request;
+
+    private PrintWriter _writer;
+    private AbstractCompressedStream _compressedStream;
+    private String _etag;
+    private long _contentLength=-1;
+    private boolean _noCompression;
+
+    /* ------------------------------------------------------------ */
+    public CompressedResponseWrapper(HttpServletRequest request, HttpServletResponse response)
+    {
+        super(response);
+        _request = request;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    public long getContentLength()
+    {
+        return _contentLength;
+    }
+
+    /* ------------------------------------------------------------ */
+    public int getBufferSize()
+    {
+        return _bufferSize;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public int getMinCompressSize()
+    {
+        return _minCompressSize;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public String getETag()
+    {
+        return _etag;
+    }
+
+    /* ------------------------------------------------------------ */
+    public HttpServletRequest getRequest()
+    {
+        return _request;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#setMimeTypes(java.util.Set)
+     */
+    public void setMimeTypes(Set<String> mimeTypes)
+    {
+        _mimeTypes = mimeTypes;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#setBufferSize(int)
+     */
+    @Override
+    public void setBufferSize(int bufferSize)
+    {
+        _bufferSize = bufferSize;
+        if (_compressedStream!=null)
+            _compressedStream.setBufferSize(bufferSize);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#setMinCompressSize(int)
+     */
+    public void setMinCompressSize(int minCompressSize)
+    {
+        _minCompressSize = minCompressSize;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#setContentType(java.lang.String)
+     */
+    @Override
+    public void setContentType(String ct)
+    {
+        super.setContentType(ct);
+    
+        if (!_noCompression)
+        {
+            if (ct!=null)
+            {
+                int colon=ct.indexOf(";");
+                if (colon>0)
+                    ct=ct.substring(0,colon);
+            }
+
+            if ((_compressedStream==null || _compressedStream.getOutputStream()==null) && 
+                    (_mimeTypes==null && ct!=null && ct.contains("gzip") ||
+                    _mimeTypes!=null && (ct==null||!_mimeTypes.contains(StringUtil.asciiToLowerCase(ct)))))
+            {
+                noCompression();
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#setStatus(int, java.lang.String)
+     */
+    @Override
+    public void setStatus(int sc, String sm)
+    {
+        super.setStatus(sc,sm);
+        if (sc<200 || sc==204 || sc==205 || sc>=300)
+            noCompression();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#setStatus(int)
+     */
+    @Override
+    public void setStatus(int sc)
+    {
+        super.setStatus(sc);
+        if (sc<200 || sc==204 || sc==205 || sc>=300)
+            noCompression();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#setContentLength(int)
+     */
+    @Override
+    public void setContentLength(int length)
+    {
+        if (_noCompression)
+            super.setContentLength(length);
+        else
+            setContentLength((long)length);
+    }
+    
+    /* ------------------------------------------------------------ */
+    protected void setContentLength(long length)
+    {
+        _contentLength=length;
+        if (_compressedStream!=null)
+            _compressedStream.setContentLength();
+        else if (_noCompression && _contentLength>=0)
+        {
+            HttpServletResponse response = (HttpServletResponse)getResponse();
+            if(_contentLength<Integer.MAX_VALUE)
+            {
+                response.setContentLength((int)_contentLength);
+            }
+            else
+            {
+                response.setHeader("Content-Length", Long.toString(_contentLength));
+            }
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#addHeader(java.lang.String, java.lang.String)
+     */
+    @Override
+    public void addHeader(String name, String value)
+    {
+        if ("content-length".equalsIgnoreCase(name))
+        {
+            _contentLength=Long.parseLong(value);
+            if (_compressedStream!=null)
+                _compressedStream.setContentLength();
+        }
+        else if ("content-type".equalsIgnoreCase(name))
+        {   
+            setContentType(value);
+        }
+        else if ("content-encoding".equalsIgnoreCase(name))
+        {   
+            super.addHeader(name,value);
+            if (!isCommitted())
+            {
+                noCompression();
+            }
+        }
+        else if ("etag".equalsIgnoreCase(name))
+            _etag=value;
+        else
+            super.addHeader(name,value);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#flushBuffer()
+     */
+    @Override
+    public void flushBuffer() throws IOException
+    {
+        if (_writer!=null)
+            _writer.flush();
+        if (_compressedStream!=null)
+            _compressedStream.flush();
+        else
+            getResponse().flushBuffer();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#reset()
+     */
+    @Override
+    public void reset()
+    {
+        super.reset();
+        if (_compressedStream!=null)
+            _compressedStream.resetBuffer();
+        _writer=null;
+        _compressedStream=null;
+        _noCompression=false;
+        _contentLength=-1;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#resetBuffer()
+     */
+    @Override
+    public void resetBuffer()
+    {
+        super.resetBuffer();
+        if (_compressedStream!=null)
+            _compressedStream.resetBuffer();
+        _writer=null;
+        _compressedStream=null;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#sendError(int, java.lang.String)
+     */
+    @Override
+    public void sendError(int sc, String msg) throws IOException
+    {
+        resetBuffer();
+        super.sendError(sc,msg);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#sendError(int)
+     */
+    @Override
+    public void sendError(int sc) throws IOException
+    {
+        resetBuffer();
+        super.sendError(sc);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#sendRedirect(java.lang.String)
+     */
+    @Override
+    public void sendRedirect(String location) throws IOException
+    {
+        resetBuffer();
+        super.sendRedirect(location);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#noCompression()
+     */
+    public void noCompression()
+    {
+        if (!_noCompression)
+            setDeferredHeaders();
+        _noCompression=true;
+        if (_compressedStream!=null)
+        {
+            try
+            {
+                _compressedStream.doNotCompress(false);
+            }
+            catch (IOException e)
+            {
+                throw new IllegalStateException(e);
+            }
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#finish()
+     */
+    public void finish() throws IOException
+    {
+        if (_writer!=null && !_compressedStream.isClosed())
+            _writer.flush();
+        if (_compressedStream!=null)
+            _compressedStream.finish();
+        else 
+            setDeferredHeaders();
+    }
+
+    /* ------------------------------------------------------------ */
+    private void setDeferredHeaders()
+    {
+        if (!isCommitted())
+        {
+            if (_contentLength>=0)
+            {
+                if (_contentLength < Integer.MAX_VALUE)
+                    super.setContentLength((int)_contentLength);
+                else
+                    super.setHeader("Content-Length",Long.toString(_contentLength));
+            }
+            if(_etag!=null)
+                super.setHeader("ETag",_etag);
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#setHeader(java.lang.String, java.lang.String)
+     */
+    @Override
+    public void setHeader(String name, String value)
+    {
+        if (_noCompression)
+            super.setHeader(name,value);
+        else if ("content-length".equalsIgnoreCase(name))
+        {
+            setContentLength(Long.parseLong(value));
+        }
+        else if ("content-type".equalsIgnoreCase(name))
+        {   
+            setContentType(value);
+        }
+        else if ("content-encoding".equalsIgnoreCase(name))
+        {   
+            super.setHeader(name,value);
+            if (!isCommitted())
+            {
+                noCompression();
+            }
+        }
+        else if ("etag".equalsIgnoreCase(name))
+            _etag=value;
+        else
+            super.setHeader(name,value);
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public boolean containsHeader(String name)
+    {
+        if (!_noCompression && "etag".equalsIgnoreCase(name) && _etag!=null)
+            return true;
+        return super.containsHeader(name);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#getOutputStream()
+     */
+    @Override
+    public ServletOutputStream getOutputStream() throws IOException
+    {
+        if (_compressedStream==null)
+        {
+            if (getResponse().isCommitted() || _noCompression)
+                return getResponse().getOutputStream();
+            
+            _compressedStream=newCompressedStream(_request,(HttpServletResponse)getResponse());
+        }
+        else if (_writer!=null)
+            throw new IllegalStateException("getWriter() called");
+        
+        return _compressedStream;   
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#getWriter()
+     */
+    @Override
+    public PrintWriter getWriter() throws IOException
+    {
+        if (_writer==null)
+        { 
+            if (_compressedStream!=null)
+                throw new IllegalStateException("getOutputStream() called");
+            
+            if (getResponse().isCommitted() || _noCompression)
+                return getResponse().getWriter();
+            
+            _compressedStream=newCompressedStream(_request,(HttpServletResponse)getResponse());
+            _writer=newWriter(_compressedStream,getCharacterEncoding());
+        }
+        return _writer;   
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.http.gzip.CompressedResponseWrapper#setIntHeader(java.lang.String, int)
+     */
+    @Override
+    public void setIntHeader(String name, int value)
+    {
+        if ("content-length".equalsIgnoreCase(name))
+        {
+            _contentLength=value;
+            if (_compressedStream!=null)
+                _compressedStream.setContentLength();
+        }
+        else
+            super.setIntHeader(name,value);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Allows derived implementations to replace PrintWriter implementation.
+     *
+     * @param out the out
+     * @param encoding the encoding
+     * @return the prints the writer
+     * @throws UnsupportedEncodingException the unsupported encoding exception
+     */
+    protected PrintWriter newWriter(OutputStream out,String encoding) throws UnsupportedEncodingException
+    {
+        return encoding==null?new PrintWriter(out):new PrintWriter(new OutputStreamWriter(out,encoding));
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     *@return the underlying CompressedStream implementation 
+     */
+    protected abstract AbstractCompressedStream newCompressedStream(HttpServletRequest _request, HttpServletResponse response) throws IOException;
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/http/mime.properties	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,181 @@
+ai	= application/postscript
+aif	= audio/x-aiff
+aifc	= audio/x-aiff
+aiff	= audio/x-aiff
+apk = application/vnd.android.package-archive
+asc	= text/plain
+asf     = video/x.ms.asf
+asx     = video/x.ms.asx
+au	= audio/basic
+avi	= video/x-msvideo
+bcpio	= application/x-bcpio
+bin	= application/octet-stream
+cab     = application/x-cabinet
+cdf	= application/x-netcdf
+class	= application/java-vm
+cpio	= application/x-cpio
+cpt	= application/mac-compactpro
+crt	= application/x-x509-ca-cert
+csh	= application/x-csh
+css	= text/css
+csv     = text/comma-separated-values
+dcr	= application/x-director
+dir	= application/x-director
+dll     = application/x-msdownload
+dms	= application/octet-stream
+doc	= application/msword
+dtd     = application/xml-dtd
+dvi	= application/x-dvi
+dxr	= application/x-director
+eps	= application/postscript
+etx	= text/x-setext
+exe	= application/octet-stream
+ez	= application/andrew-inset
+gif	= image/gif
+gtar	= application/x-gtar
+gz	= application/gzip
+gzip	= application/gzip
+hdf	= application/x-hdf
+hqx	= application/mac-binhex40
+htc = text/x-component
+htm	= text/html
+html	= text/html
+ice	= x-conference/x-cooltalk
+ico	= image/x-icon
+ief	= image/ief
+iges	= model/iges
+igs	= model/iges
+jad     = text/vnd.sun.j2me.app-descriptor
+jar     = application/java-archive
+java	= text/plain
+jnlp	= application/x-java-jnlp-file
+jpe	= image/jpeg
+jpeg	= image/jpeg
+jpg	= image/jpeg
+js	= application/x-javascript
+jsp	= text/html
+kar	= audio/midi
+latex	= application/x-latex
+lha	= application/octet-stream
+lzh	= application/octet-stream
+man	= application/x-troff-man
+mathml	= application/mathml+xml
+me	= application/x-troff-me
+mesh	= model/mesh
+mid	= audio/midi
+midi	= audio/midi
+mif	= application/vnd.mif
+mol     = chemical/x-mdl-molfile
+mov	= video/quicktime
+movie	= video/x-sgi-movie
+mp2	= audio/mpeg
+mp3	= audio/mpeg
+mpe	= video/mpeg
+mpeg	= video/mpeg
+mpg	= video/mpeg
+mpga	= audio/mpeg
+ms	= application/x-troff-ms
+msh	= model/mesh
+msi	= application/octet-stream
+nc	= application/x-netcdf
+oda	= application/oda
+odb     = application/vnd.oasis.opendocument.database
+odc     = application/vnd.oasis.opendocument.chart
+odf     = application/vnd.oasis.opendocument.formula
+odg     = application/vnd.oasis.opendocument.graphics
+odi     = application/vnd.oasis.opendocument.image
+odm     = application/vnd.oasis.opendocument.text-master
+odp     = application/vnd.oasis.opendocument.presentation
+ods     = application/vnd.oasis.opendocument.spreadsheet
+odt     = application/vnd.oasis.opendocument.text
+ogg	= application/ogg
+otc     = application/vnd.oasis.opendocument.chart-template
+otf     = application/vnd.oasis.opendocument.formula-template
+otg     = application/vnd.oasis.opendocument.graphics-template
+oth     = application/vnd.oasis.opendocument.text-web
+oti     = application/vnd.oasis.opendocument.image-template
+otp     = application/vnd.oasis.opendocument.presentation-template
+ots     = application/vnd.oasis.opendocument.spreadsheet-template
+ott     = application/vnd.oasis.opendocument.text-template
+pbm	= image/x-portable-bitmap
+pdb	= chemical/x-pdb
+pdf	= application/pdf
+pgm	= image/x-portable-graymap
+pgn	= application/x-chess-pgn
+png	= image/png
+pnm	= image/x-portable-anymap
+ppm	= image/x-portable-pixmap
+pps     = application/vnd.ms-powerpoint
+ppt	= application/vnd.ms-powerpoint
+ps	= application/postscript
+qt	= video/quicktime
+ra	= audio/x-pn-realaudio
+ram	= audio/x-pn-realaudio
+ras	= image/x-cmu-raster
+rdf	= application/rdf+xml
+rgb	= image/x-rgb
+rm	= audio/x-pn-realaudio
+roff	= application/x-troff
+rpm	= application/x-rpm
+rtf	= application/rtf
+rtx	= text/richtext
+rv      = video/vnd.rn-realvideo
+ser     = application/java-serialized-object
+sgm	= text/sgml
+sgml	= text/sgml
+sh	= application/x-sh
+shar	= application/x-shar
+silo	= model/mesh
+sit	= application/x-stuffit
+skd	= application/x-koan
+skm	= application/x-koan
+skp	= application/x-koan
+skt	= application/x-koan
+smi	= application/smil
+smil	= application/smil
+snd	= audio/basic
+spl	= application/x-futuresplash
+src	= application/x-wais-source
+sv4cpio	= application/x-sv4cpio
+sv4crc	= application/x-sv4crc
+svg	= image/svg+xml
+swf	= application/x-shockwave-flash
+t	= application/x-troff
+tar	= application/x-tar
+tar.gz	= application/x-gtar
+tcl	= application/x-tcl
+tex	= application/x-tex
+texi	= application/x-texinfo
+texinfo	= application/x-texinfo
+tgz	= application/x-gtar
+tif	= image/tiff
+tiff	= image/tiff
+tr	= application/x-troff
+tsv	= text/tab-separated-values
+txt	= text/plain
+ustar	= application/x-ustar
+vcd	= application/x-cdlink
+vrml	= model/vrml
+vxml	= application/voicexml+xml
+wav	= audio/x-wav
+wbmp	= image/vnd.wap.wbmp
+wml	= text/vnd.wap.wml
+wmlc	= application/vnd.wap.wmlc
+wmls	= text/vnd.wap.wmlscript
+wmlsc	= application/vnd.wap.wmlscriptc
+wrl	= model/vrml
+wtls-ca-certificate	= application/vnd.wap.wtls-ca-certificate
+xbm	= image/x-xbitmap
+xht	= application/xhtml+xml
+xhtml	= application/xhtml+xml
+xls	= application/vnd.ms-excel
+xml	= application/xml
+xpm	= image/x-xpixmap
+xsd	= application/xml
+xsl	= application/xml
+xslt	= application/xslt+xml
+xul	= application/vnd.mozilla.xul+xml
+xwd	= image/x-xwindowdump
+xyz	= chemical/x-xyz
+z	= application/compress
+zip	= application/zip
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/http/ssl/SslContextFactory.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,41 @@
+//
+//  ========================================================================
+//  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.http.ssl;
+
+/* ------------------------------------------------------------ */
+/**
+ * @deprecated Use org.eclipse.jetty.util.ssl.SslContextFactory
+ */
+public class SslContextFactory extends org.eclipse.jetty.util.ssl.SslContextFactory
+{
+    public SslContextFactory()
+    {
+        super();
+    }
+
+    public SslContextFactory(boolean trustAll)
+    {
+        super(trustAll);
+    }
+
+    public SslContextFactory(String keyStorePath)
+    {
+        super(keyStorePath);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/AbstractBuffer.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,728 @@
+//
+//  ========================================================================
+//  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.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.Charset;
+
+import org.eclipse.jetty.util.TypeUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * 
+ *  
+ */
+public abstract class AbstractBuffer implements Buffer
+{
+    private static final Logger LOG = Log.getLogger(AbstractBuffer.class);
+
+    private final static boolean __boundsChecking = Boolean.getBoolean("org.eclipse.jetty.io.AbstractBuffer.boundsChecking");
+    
+    protected final static String 
+    __IMMUTABLE = "IMMUTABLE", 
+    __READONLY = "READONLY",
+    __READWRITE = "READWRITE", 
+    __VOLATILE = "VOLATILE";
+    
+    protected int _access;
+    protected boolean _volatile;
+
+    protected int _get;
+    protected int _put;
+    protected int _hash;
+    protected int _hashGet;
+    protected int _hashPut;
+    protected int _mark;
+    protected String _string;
+    protected View _view;
+
+    /**
+     * Constructor for BufferView
+     * 
+     * @param access 0==IMMUTABLE, 1==READONLY, 2==READWRITE
+     */
+    public AbstractBuffer(int access, boolean isVolatile)
+    {
+        if (access == IMMUTABLE && isVolatile)
+                throw new IllegalArgumentException("IMMUTABLE && VOLATILE");
+        setMarkIndex(-1);
+        _access = access;
+        _volatile = isVolatile;
+    }
+
+    /*
+     * @see org.eclipse.io.Buffer#toArray()
+     */
+    public byte[] asArray()
+    {
+        byte[] bytes = new byte[length()];
+        byte[] array = array();
+        if (array != null)
+            System.arraycopy(array, getIndex(), bytes, 0, bytes.length);
+        else
+            peek(getIndex(), bytes, 0, length());
+        return bytes;
+    }
+
+    public ByteArrayBuffer duplicate(int access)
+    {
+        Buffer b=this.buffer();
+        if (this instanceof Buffer.CaseInsensitve || b instanceof Buffer.CaseInsensitve)
+            return new ByteArrayBuffer.CaseInsensitive(asArray(), 0, length(),access);
+        else
+            return new ByteArrayBuffer(asArray(), 0, length(), access);
+    }
+    
+    /*
+     * @see org.eclipse.io.Buffer#asNonVolatile()
+     */
+    public Buffer asNonVolatileBuffer()
+    {
+        if (!isVolatile()) return this;
+        return duplicate(_access);
+    }
+
+    public Buffer asImmutableBuffer()
+    {
+        if (isImmutable()) return this;
+        return duplicate(IMMUTABLE);
+    }
+
+    /*
+     * @see org.eclipse.util.Buffer#asReadOnlyBuffer()
+     */
+    public Buffer asReadOnlyBuffer()
+    {
+        if (isReadOnly()) return this;
+        return new View(this, markIndex(), getIndex(), putIndex(), READONLY);
+    }
+
+    public Buffer asMutableBuffer()
+    {
+        if (!isImmutable()) return this;
+        
+        Buffer b=this.buffer();
+        if (b.isReadOnly())
+        {
+            return duplicate(READWRITE);
+        }
+        return new View(b, markIndex(), getIndex(), putIndex(), _access);
+    }
+
+    public Buffer buffer()
+    {
+        return this;
+    }
+
+    public void clear()
+    {
+        setMarkIndex(-1);
+        setGetIndex(0);
+        setPutIndex(0);
+    }
+
+    public void compact()
+    {
+        if (isReadOnly()) throw new IllegalStateException(__READONLY);
+        int s = markIndex() >= 0 ? markIndex() : getIndex();
+        if (s > 0)
+        {
+            byte array[] = array();
+            int length = putIndex() - s;
+            if (length > 0)
+            {
+                if (array != null)
+                    System.arraycopy(array(), s, array(), 0, length);
+                else
+                    poke(0, peek(s, length));
+            }
+            if (markIndex() > 0) setMarkIndex(markIndex() - s);
+            setGetIndex(getIndex() - s);
+            setPutIndex(putIndex() - s);
+        }
+    }
+
+    @Override
+    public boolean equals(Object obj)
+    {
+        if (obj==this)
+            return true;
+        
+        // reject non buffers;
+        if (obj == null || !(obj instanceof Buffer)) return false;
+        Buffer b = (Buffer) obj;
+
+        if (this instanceof Buffer.CaseInsensitve ||  b instanceof Buffer.CaseInsensitve)
+            return equalsIgnoreCase(b);
+        
+        // reject different lengths
+        if (b.length() != length()) return false;
+
+        // reject AbstractBuffer with different hash value
+        if (_hash != 0 && obj instanceof AbstractBuffer)
+        {
+            AbstractBuffer ab = (AbstractBuffer) obj;
+            if (ab._hash != 0 && _hash != ab._hash) return false;
+        }
+
+        // Nothing for it but to do the hard grind.
+        int get=getIndex();
+        int bi=b.putIndex();
+        for (int i = putIndex(); i-->get;)
+        {
+            byte b1 = peek(i);
+            byte b2 = b.peek(--bi);
+            if (b1 != b2) return false;
+        }
+        return true;
+    }
+
+    public boolean equalsIgnoreCase(Buffer b)
+    {
+        if (b==this)
+            return true;
+        
+        // reject different lengths
+        if (b.length() != length()) return false;
+
+        // reject AbstractBuffer with different hash value
+        if (_hash != 0 && b instanceof AbstractBuffer)
+        {
+            AbstractBuffer ab = (AbstractBuffer) b;
+            if (ab._hash != 0 && _hash != ab._hash) return false;
+        }
+
+        // Nothing for it but to do the hard grind.
+        int get=getIndex();
+        int bi=b.putIndex();
+        
+        byte[] array = array();
+        byte[] barray= b.array();
+        if (array!=null && barray!=null)
+        {
+            for (int i = putIndex(); i-->get;)
+            {
+                byte b1 = array[i];
+                byte b2 = barray[--bi];
+                if (b1 != b2)
+                {
+                    if ('a' <= b1 && b1 <= 'z') b1 = (byte) (b1 - 'a' + 'A');
+                    if ('a' <= b2 && b2 <= 'z') b2 = (byte) (b2 - 'a' + 'A');
+                    if (b1 != b2) return false;
+                }
+            }
+        }
+        else
+        {
+            for (int i = putIndex(); i-->get;)
+            {
+                byte b1 = peek(i);
+                byte b2 = b.peek(--bi);
+                if (b1 != b2)
+                {
+                    if ('a' <= b1 && b1 <= 'z') b1 = (byte) (b1 - 'a' + 'A');
+                    if ('a' <= b2 && b2 <= 'z') b2 = (byte) (b2 - 'a' + 'A');
+                    if (b1 != b2) return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    public byte get()
+    {
+        return peek(_get++);
+    }
+
+    public int get(byte[] b, int offset, int length)
+    {
+        int gi = getIndex();
+        int l=length();
+        if (l==0)
+            return -1;
+        
+        if (length>l)
+            length=l;
+        
+        length = peek(gi, b, offset, length);
+        if (length>0)
+            setGetIndex(gi + length);
+        return length;
+    }
+
+    public Buffer get(int length)
+    {
+        int gi = getIndex();
+        Buffer view = peek(gi, length);
+        setGetIndex(gi + length);
+        return view;
+    }
+
+    public final int getIndex()
+    {
+        return _get;
+    }
+
+    public boolean hasContent()
+    {
+        return _put > _get;
+    }
+    
+    @Override
+    public int hashCode()
+    {
+        if (_hash == 0 || _hashGet!=_get || _hashPut!=_put) 
+        {
+            int get=getIndex();
+            byte[] array = array();
+            if (array==null)
+            {
+                for (int i = putIndex(); i-- >get;)
+                {
+                    byte b = peek(i);
+                    if ('a' <= b && b <= 'z') 
+                        b = (byte) (b - 'a' + 'A');
+                    _hash = 31 * _hash + b;
+                }
+            }
+            else
+            {
+                for (int i = putIndex(); i-- >get;)
+                {
+                    byte b = array[i];
+                    if ('a' <= b && b <= 'z') 
+                        b = (byte) (b - 'a' + 'A');
+                    _hash = 31 * _hash + b;
+                }
+            }
+            if (_hash == 0) 
+                _hash = -1;
+            _hashGet=_get;
+            _hashPut=_put;
+            
+        }
+        return _hash;
+    }
+
+    public boolean isImmutable()
+    {
+        return _access <= IMMUTABLE;
+    }
+
+    public boolean isReadOnly()
+    {
+        return _access <= READONLY;
+    }
+
+    public boolean isVolatile()
+    {
+        return _volatile;
+    }
+
+    public int length()
+    {
+        return _put - _get;
+    }
+
+    public void mark()
+    {
+        setMarkIndex(_get - 1);
+    }
+
+    public void mark(int offset)
+    {
+        setMarkIndex(_get + offset);
+    }
+
+    public int markIndex()
+    {
+        return _mark;
+    }
+
+    public byte peek()
+    {
+        return peek(_get);
+    }
+
+    public Buffer peek(int index, int length)
+    {
+        if (_view == null)
+        {
+            _view = new View(this, -1, index, index + length, isReadOnly() ? READONLY : READWRITE);
+        }
+        else
+        {
+            _view.update(this.buffer());
+            _view.setMarkIndex(-1);
+            _view.setGetIndex(0);
+            _view.setPutIndex(index + length);
+            _view.setGetIndex(index);
+            
+        }
+        return _view;
+    }
+
+    public int poke(int index, Buffer src)
+    {
+        _hash=0;
+        /* 
+        if (isReadOnly()) 
+            throw new IllegalStateException(__READONLY);
+        if (index < 0) 
+            throw new IllegalArgumentException("index<0: " + index + "<0");
+        */
+        
+        int length=src.length();
+        if (index + length > capacity())
+        {
+            length=capacity()-index;
+            /*
+            if (length<0)
+                throw new IllegalArgumentException("index>capacity(): " + index + ">" + capacity());
+            */
+        }
+        
+        byte[] src_array = src.array();
+        byte[] dst_array = array();
+        if (src_array != null && dst_array != null)
+            System.arraycopy(src_array, src.getIndex(), dst_array, index, length);
+        else if (src_array != null)
+        {
+            int s=src.getIndex();
+            for (int i=0;i<length;i++)
+                poke(index++,src_array[s++]);
+        }
+        else if (dst_array != null)
+        {
+            int s=src.getIndex();
+            for (int i=0;i<length;i++)
+                dst_array[index++]=src.peek(s++);
+        }
+        else
+        {
+            int s=src.getIndex();
+            for (int i=0;i<length;i++)
+                poke(index++,src.peek(s++));
+        }
+        
+        return length;
+    }
+    
+
+    public int poke(int index, byte[] b, int offset, int length)
+    {
+        _hash=0;
+        /*
+        if (isReadOnly()) 
+            throw new IllegalStateException(__READONLY);
+        if (index < 0) 
+            throw new IllegalArgumentException("index<0: " + index + "<0");
+        */
+        if (index + length > capacity())
+        {
+            length=capacity()-index;
+            /* if (length<0)
+                throw new IllegalArgumentException("index>capacity(): " + index + ">" + capacity());
+            */
+        }
+        
+        byte[] dst_array = array();
+        if (dst_array != null)
+            System.arraycopy(b, offset, dst_array, index, length);
+        else
+        {
+            int s=offset;
+            for (int i=0;i<length;i++)
+                poke(index++,b[s++]);
+        }
+        return length;
+    }
+
+    public int put(Buffer src)
+    {
+        int pi = putIndex();
+        int l=poke(pi, src);
+        setPutIndex(pi + l);
+        return l;
+    }
+
+    public void put(byte b)
+    {
+        int pi = putIndex();
+        poke(pi, b);
+        setPutIndex(pi + 1);
+    }
+
+    public int put(byte[] b, int offset, int length)
+    {
+        int pi = putIndex();
+        int l = poke(pi, b, offset, length);
+        setPutIndex(pi + l);
+        return l;
+    }
+    
+    public int put(byte[] b)
+    {
+        int pi = putIndex();
+        int l = poke(pi, b, 0, b.length);
+        setPutIndex(pi + l);
+        return l;
+    }
+
+    public final int putIndex()
+    {
+        return _put;
+    }
+
+    public void reset()
+    {
+        if (markIndex() >= 0) setGetIndex(markIndex());
+    }
+
+    public void rewind()
+    {
+        setGetIndex(0);
+        setMarkIndex(-1);
+    }
+
+    public void setGetIndex(int getIndex)
+    {
+        /* bounds checking
+        if (isImmutable()) 
+            throw new IllegalStateException(__IMMUTABLE);
+        if (getIndex < 0)
+            throw new IllegalArgumentException("getIndex<0: " + getIndex + "<0");
+        if (getIndex > putIndex())
+            throw new IllegalArgumentException("getIndex>putIndex: " + getIndex + ">" + putIndex());
+         */
+        _get = getIndex;
+        _hash=0;
+    }
+
+    public void setMarkIndex(int index)
+    {
+        /*
+        if (index>=0 && isImmutable()) 
+            throw new IllegalStateException(__IMMUTABLE);
+        */
+        _mark = index;
+    }
+
+    public void setPutIndex(int putIndex)
+    {
+        /* bounds checking
+        if (isImmutable()) 
+            throw new IllegalStateException(__IMMUTABLE);
+        if (putIndex > capacity())
+                throw new IllegalArgumentException("putIndex>capacity: " + putIndex + ">" + capacity());
+        if (getIndex() > putIndex)
+                throw new IllegalArgumentException("getIndex>putIndex: " + getIndex() + ">" + putIndex);
+         */
+        _put = putIndex;
+        _hash=0;
+    }
+
+    public int skip(int n)
+    {
+        if (length() < n) n = length();
+        setGetIndex(getIndex() + n);
+        return n;
+    }
+
+    public Buffer slice()
+    {
+        return peek(getIndex(), length());
+    }
+
+    public Buffer sliceFromMark()
+    {
+        return sliceFromMark(getIndex() - markIndex() - 1);
+    }
+
+    public Buffer sliceFromMark(int length)
+    {
+        if (markIndex() < 0) return null;
+        Buffer view = peek(markIndex(), length);
+        setMarkIndex(-1);
+        return view;
+    }
+
+    public int space()
+    {
+        return capacity() - _put;
+    }
+
+    public String toDetailString()
+    {
+        StringBuilder buf = new StringBuilder();
+        buf.append("[");
+        buf.append(super.hashCode());
+        buf.append(",");
+        buf.append(this.buffer().hashCode());
+        buf.append(",m=");
+        buf.append(markIndex());
+        buf.append(",g=");
+        buf.append(getIndex());
+        buf.append(",p=");
+        buf.append(putIndex());
+        buf.append(",c=");
+        buf.append(capacity());
+        buf.append("]={");
+        if (markIndex() >= 0)
+        {
+            for (int i = markIndex(); i < getIndex(); i++)
+            {
+                byte b =  peek(i);
+                TypeUtil.toHex(b,buf);
+            }
+            buf.append("}{");
+        }
+        int count = 0;
+        for (int i = getIndex(); i < putIndex(); i++)
+        {
+            byte b =  peek(i);
+            TypeUtil.toHex(b,buf);
+            if (count++ == 50)
+            {
+                if (putIndex() - i > 20)
+                {
+                    buf.append(" ... ");
+                    i = putIndex() - 20;
+                }
+            }
+        }
+        buf.append('}');
+        return buf.toString();
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public String toString()
+    {
+        if (isImmutable())
+        {
+            if (_string == null) 
+                _string = new String(asArray(), 0, length());
+            return _string;
+        }
+        return new String(asArray(), 0, length());
+    }
+
+    /* ------------------------------------------------------------ */
+    public String toString(String charset)
+    {
+        try
+        {
+            byte[] bytes=array();
+            if (bytes!=null)
+                return new String(bytes,getIndex(),length(),charset);
+            return new String(asArray(), 0, length(),charset);
+            
+        }
+        catch(Exception e)
+        {
+            LOG.warn(e);
+            return new String(asArray(), 0, length());
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public String toString(Charset charset)
+    {
+        try
+        {
+            byte[] bytes=array();
+            if (bytes!=null)
+                return new String(bytes,getIndex(),length(),charset);
+            return new String(asArray(), 0, length(),charset);
+        }
+        catch(Exception e)
+        {
+            LOG.warn(e);
+            return new String(asArray(), 0, length());
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public String toDebugString()
+    {
+        return getClass()+"@"+super.hashCode();
+    }
+
+    /* ------------------------------------------------------------ */
+    public void writeTo(OutputStream out)
+    	throws IOException
+    {
+        byte[] array = array();
+        
+        if (array!=null)
+        {
+            out.write(array,getIndex(),length());
+        }
+        else
+        {
+            int len = this.length();
+            byte[] buf=new byte[len>1024?1024:len];
+            int offset=_get;
+            while (len>0)
+            {
+                int l=peek(offset,buf,0,len>buf.length?buf.length:len);
+                out.write(buf,0,l);
+                offset+=l;
+                len-=l;
+            }
+        } 
+        clear();
+    }
+    
+    /* ------------------------------------------------------------ */
+    public int readFrom(InputStream in,int max) throws IOException
+    {
+        byte[] array = array();
+        int s=space();
+        if (s>max)
+            s=max;
+
+        if (array!=null)
+        {
+            int l=in.read(array,_put,s);
+            if (l>0)
+                _put+=l;
+            return l;
+        }
+        else
+        {
+            byte[] buf=new byte[s>1024?1024:s];
+            int total=0;
+            while (s>0)
+            {
+                int l=in.read(buf,0,buf.length);
+                if (l<0)
+                    return total>0?total:-1;
+                int p=put(buf,0,l);
+                assert l==p;
+                s-=l;
+            }
+            return total; 
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/AbstractBuffers.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,168 @@
+//
+//  ========================================================================
+//  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.io;
+
+import org.eclipse.jetty.io.nio.DirectNIOBuffer;
+import org.eclipse.jetty.io.nio.IndirectNIOBuffer;
+
+public abstract class AbstractBuffers implements Buffers
+{
+    protected final Buffers.Type _headerType;
+    protected final int _headerSize;
+    protected final Buffers.Type _bufferType;
+    protected final int _bufferSize;
+    protected final Buffers.Type _otherType;
+
+    /* ------------------------------------------------------------ */
+    public AbstractBuffers(Buffers.Type headerType, int headerSize, Buffers.Type bufferType, int bufferSize, Buffers.Type otherType)
+    {
+        _headerType=headerType;
+        _headerSize=headerSize;
+        _bufferType=bufferType;
+        _bufferSize=bufferSize;
+        _otherType=otherType;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the buffer size in bytes.
+     */
+    public int getBufferSize()
+    {
+        return _bufferSize;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the header size in bytes.
+     */
+    public int getHeaderSize()
+    {
+        return _headerSize;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Create a new header Buffer
+     * @return new Buffer
+     */
+    final protected Buffer newHeader()
+    {
+        switch(_headerType)
+        {
+            case BYTE_ARRAY:
+                return new ByteArrayBuffer(_headerSize);
+            case DIRECT:
+                return new DirectNIOBuffer(_headerSize);
+            case INDIRECT:
+                return new IndirectNIOBuffer(_headerSize);
+        }
+        throw new IllegalStateException();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Create a new content Buffer
+     * @return new Buffer
+     */
+    final protected Buffer newBuffer()
+    {
+       switch(_bufferType)
+       {
+           case BYTE_ARRAY:
+               return new ByteArrayBuffer(_bufferSize);
+           case DIRECT:
+               return new DirectNIOBuffer(_bufferSize);
+           case INDIRECT:
+               return new IndirectNIOBuffer(_bufferSize);
+       }
+       throw new IllegalStateException();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Create a new content Buffer
+     * @param size
+     * @return new Buffer
+     */
+    final protected Buffer newBuffer(int size)
+    {
+       switch(_otherType)
+       {
+           case BYTE_ARRAY:
+               return new ByteArrayBuffer(size);
+           case DIRECT:
+               return new DirectNIOBuffer(size);
+           case INDIRECT:
+               return new IndirectNIOBuffer(size);
+       }
+       throw new IllegalStateException();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param buffer
+     * @return True if the buffer is the correct type to be a Header buffer
+     */
+    public final boolean isHeader(Buffer buffer)
+    {
+        if (buffer.capacity()==_headerSize)
+        {
+            switch(_headerType)
+            {
+                case BYTE_ARRAY:
+                    return buffer instanceof ByteArrayBuffer && !(buffer instanceof  IndirectNIOBuffer);
+                case DIRECT:
+                    return buffer instanceof  DirectNIOBuffer;
+                case INDIRECT:
+                    return buffer instanceof  IndirectNIOBuffer;
+            }
+        }
+        return false;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param buffer
+     * @return True if the buffer is the correct type to be a Header buffer
+     */
+    public final boolean isBuffer(Buffer buffer)
+    {
+        if (buffer.capacity()==_bufferSize)
+        {
+            switch(_bufferType)
+            {
+                case BYTE_ARRAY:
+                    return buffer instanceof ByteArrayBuffer && !(buffer instanceof  IndirectNIOBuffer);
+                case DIRECT:
+                    return buffer instanceof  DirectNIOBuffer;
+                case INDIRECT:
+                    return buffer instanceof  IndirectNIOBuffer;
+            }
+        }
+        return false;
+    }
+
+    /* ------------------------------------------------------------ */
+    public String toString()
+    {
+        return String.format("%s [%d,%d]", getClass().getSimpleName(), _headerSize, _bufferSize);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/AbstractConnection.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,85 @@
+//
+//  ========================================================================
+//  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.io;
+
+import java.io.IOException;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+
+public abstract class AbstractConnection implements Connection
+{
+    private static final Logger LOG = Log.getLogger(AbstractConnection.class);
+
+    private final long _timeStamp;
+    protected final EndPoint _endp;
+
+    public AbstractConnection(EndPoint endp)
+    {
+        _endp=(EndPoint)endp;
+        _timeStamp = System.currentTimeMillis();
+    }
+
+    public AbstractConnection(EndPoint endp,long timestamp)
+    {
+        _endp=(EndPoint)endp;
+        _timeStamp = timestamp;
+    }
+
+    public long getTimeStamp()
+    {
+        return _timeStamp;
+    }
+
+    public EndPoint getEndPoint()
+    {
+        return _endp;
+    }
+
+    public void onIdleExpired(long idleForMs)
+    {
+        try
+        {
+            LOG.debug("onIdleExpired {}ms {} {}",idleForMs,this,_endp);
+            if (_endp.isInputShutdown() || _endp.isOutputShutdown())
+                _endp.close();
+            else
+                _endp.shutdownOutput();
+        }
+        catch(IOException e)
+        {
+            LOG.ignore(e);
+
+            try
+            {
+                _endp.close();
+            }
+            catch(IOException e2)
+            {
+                LOG.ignore(e2);
+            }
+        }
+    }
+
+    public String toString()
+    {
+        return String.format("%s@%x", getClass().getSimpleName(), hashCode());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/AsyncEndPoint.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,83 @@
+//
+//  ========================================================================
+//  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.io;
+
+import org.eclipse.jetty.util.thread.Timeout;
+
+public interface AsyncEndPoint extends ConnectedEndPoint
+{
+    /* ------------------------------------------------------------ */
+    /**
+     * Dispatch the endpoint if it is not already dispatched
+     * 
+     */
+    public void dispatch();
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Dispatch the endpoint. If it is already dispatched, schedule a redispatch
+     * 
+     */
+    public void asyncDispatch();
+    
+    /* ------------------------------------------------------------ */
+    /** Schedule a write dispatch.
+     * Set the endpoint to not be writable and schedule a dispatch when
+     * it becomes writable.
+     */
+    public void scheduleWrite();  
+
+    /* ------------------------------------------------------------ */
+    /** Callback when idle.
+     * <p>An endpoint is idle if there has been no IO activity for 
+     * {@link #getMaxIdleTime()} and {@link #isCheckForIdle()} is true.
+     * @param idleForMs TODO
+     */
+    public void onIdleExpired(long idleForMs);
+
+    /* ------------------------------------------------------------ */
+    /** Set if the endpoint should be checked for idleness
+     */
+    public void setCheckForIdle(boolean check);
+
+    /* ------------------------------------------------------------ */
+    /** Get if the endpoint should be checked for idleness
+     */
+    public boolean isCheckForIdle();
+
+    
+    /* ------------------------------------------------------------ */
+    public boolean isWritable();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return True if IO has been successfully performed since the last call to {@link #hasProgressed()}
+     */
+    public boolean hasProgressed();
+    
+    /* ------------------------------------------------------------ */
+    /**
+     */
+    public void scheduleTimeout(Timeout.Task task, long timeoutMs);
+
+    /* ------------------------------------------------------------ */
+    /**
+     */
+    public void cancelTimeout(Timeout.Task task);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/Buffer.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,380 @@
+//
+//  ========================================================================
+//  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.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.Charset;
+
+
+/**
+ * Byte Buffer interface.
+ * 
+ * This is a byte buffer that is designed to work like a FIFO for bytes. Puts and Gets operate on different
+ * pointers into the buffer and the valid _content of the buffer is always between the getIndex and the putIndex.
+ * 
+ * This buffer interface is designed to be similar, but not dependent on the java.nio buffers, which may
+ * be used to back an implementation of this Buffer. The main difference is that NIO buffer after a put have 
+ * their valid _content before the position and a flip is required to access that data.
+ *
+ * For this buffer it is always true that:
+ *  markValue <= getIndex <= putIndex <= capacity
+ *  
+ *
+ * @version 1.0
+ */
+public interface Buffer extends Cloneable
+{
+    public final static int 
+      IMMUTABLE=0,  // neither indexes or contexts can be changed
+      READONLY=1,   // indexes may be changed, but not content
+      READWRITE=2;  // anything can be changed
+    public final boolean VOLATILE=true;     // The buffer may change outside of current scope.
+    public final boolean NON_VOLATILE=false;
+
+    /**
+     *  Get the underlying array, if one exists.
+     * @return a <code>byte[]</code> backing this buffer or null if none exists.
+     */
+    byte[] array();
+    
+    /**
+     * 
+     * @return a <code>byte[]</code> value of the bytes from the getIndex to the putIndex.
+     */
+    byte[] asArray();
+    
+    /** 
+     * Get the underlying buffer. If this buffer wraps a backing buffer.
+     * @return The root backing buffer or this if there is no backing buffer;
+     */
+    Buffer buffer();
+    
+    /**
+     * 
+     * @return a non volatile version of this <code>Buffer</code> value
+     */
+    Buffer asNonVolatileBuffer();
+
+    /**
+     *
+     * @return a readonly version of this <code>Buffer</code>.
+     */
+    Buffer asReadOnlyBuffer();
+
+    /**
+     *
+     * @return an immutable version of this <code>Buffer</code>.
+     */
+    Buffer asImmutableBuffer();
+
+    /**
+     *
+     * @return an immutable version of this <code>Buffer</code>.
+     */
+    Buffer asMutableBuffer();
+    
+    /**
+     * 
+     * The capacity of the buffer. This is the maximum putIndex that may be set.
+     * @return an <code>int</code> value
+     */
+    int capacity();
+    
+    /**
+     * the space remaining in the buffer.
+     * @return capacity - putIndex
+     */
+    int space();
+    
+    /**
+     * Clear the buffer. getIndex=0, putIndex=0.
+     */
+    void clear();
+
+    /**
+     * Compact the buffer by discarding bytes before the postion (or mark if set).
+     * Bytes from the getIndex (or mark) to the putIndex are moved to the beginning of 
+     * the buffer and the values adjusted accordingly.
+     */
+    void compact();
+    
+    /**
+     * Get the byte at the current getIndex and increment it.
+     * @return The <code>byte</code> value from the current getIndex.
+     */
+    byte get();
+    
+    /**
+     * Get bytes from the current postion and put them into the passed byte array.
+     * The getIndex is incremented by the number of bytes copied into the array.
+     * @param b The byte array to fill.
+     * @param offset Offset in the array.
+     * @param length The max number of bytes to read.
+     * @return The number of bytes actually read.
+     */
+    int get(byte[] b, int offset, int length);
+
+    /**
+     * 
+     * @param length an <code>int</code> value
+     * @return a <code>Buffer</code> value
+     */
+    Buffer get(int length);
+
+    /**
+     * The index within the buffer that will next be read or written.
+     * @return an <code>int</code> value >=0 <= putIndex()
+     */
+    int getIndex();
+    
+    /**
+     * @return true of putIndex > getIndex
+     */
+    boolean hasContent();
+    
+    /**
+     * 
+     * @return a <code>boolean</code> value true if case sensitive comparison on this buffer
+     */
+    boolean equalsIgnoreCase(Buffer buffer);
+
+
+    /**
+     * 
+     * @return a <code>boolean</code> value true if the buffer is immutable and that neither
+     * the buffer contents nor the indexes may be changed.
+     */
+    boolean isImmutable();
+    
+    /**
+     * 
+     * @return a <code>boolean</code> value true if the buffer is readonly. The buffer indexes may
+     * be modified, but the buffer contents may not. For example a View onto an immutable Buffer will be
+     * read only.
+     */
+    boolean isReadOnly();
+    
+    /**
+     * 
+     * @return a <code>boolean</code> value true if the buffer contents may change 
+     * via alternate paths than this buffer.  If the contents of this buffer are to be used outside of the
+     * current context, then a copy must be made.
+     */
+    boolean isVolatile();
+
+    /**
+     * The number of bytes from the getIndex to the putIndex
+     * @return an <code>int</code> == putIndex()-getIndex()
+     */
+    int length();
+    
+    /**
+     * Set the mark to the current getIndex.
+     */
+    void mark();
+    
+    /**
+     * Set the mark relative to the current getIndex
+     * @param offset an <code>int</code> value to add to the current getIndex to obtain the mark value.
+     */
+    void mark(int offset);
+
+    /**
+     * The current index of the mark.
+     * @return an <code>int</code> index in the buffer or -1 if the mark is not set.
+     */
+    int markIndex();
+
+    /**
+     * Get the byte at the current getIndex without incrementing the getIndex.
+     * @return The <code>byte</code> value from the current getIndex.
+     */
+    byte peek();
+  
+    /**
+     * Get the byte at a specific index in the buffer.
+     * @param index an <code>int</code> value
+     * @return a <code>byte</code> value
+     */
+    byte peek(int index);
+
+    /**
+     * 
+     * @param index an <code>int</code> value
+     * @param length an <code>int</code> value
+     * @return The <code>Buffer</code> value from the requested getIndex.
+     */
+    Buffer peek(int index, int length);
+
+    /**
+     * 
+     * @param index an <code>int</code> value
+     * @param b The byte array to peek into
+     * @param offset The offset into the array to start peeking
+     * @param length an <code>int</code> value
+     * @return The number of bytes actually peeked
+     */
+    int peek(int index, byte[] b, int offset, int length);
+    
+    /**
+     * Put the contents of the buffer at the specific index.
+     * @param index an <code>int</code> value
+     * @param src a <code>Buffer</code>. If the source buffer is not modified
+    
+     * @return The number of bytes actually poked
+     */
+    int poke(int index, Buffer src);
+    
+    /**
+     * Put a specific byte to a specific getIndex.
+     * @param index an <code>int</code> value
+     * @param b a <code>byte</code> value
+     */
+    void poke(int index, byte b);
+    
+    /**
+     * Put a specific byte to a specific getIndex.
+     * @param index an <code>int</code> value
+     * @param b a <code>byte array</code> value
+     * @return The number of bytes actually poked
+     */
+    int poke(int index, byte b[], int offset, int length);
+    
+    /**
+     * Write the bytes from the source buffer to the current getIndex.
+     * @param src The source <code>Buffer</code> it is not modified.
+     * @return The number of bytes actually poked
+     */
+    int put(Buffer src);
+
+    /**
+     * Put a byte to the current getIndex and increment the getIndex.
+     * @param b a <code>byte</code> value
+     */
+    void put(byte b);
+    
+    /**
+     * Put a byte to the current getIndex and increment the getIndex.
+     * @param b a <code>byte</code> value
+     * @return The number of bytes actually poked
+     */
+    int put(byte[] b,int offset, int length);
+
+    /**
+     * Put a byte to the current getIndex and increment the getIndex.
+     * @param b a <code>byte</code> value
+     * @return The number of bytes actually poked
+     */
+    int put(byte[] b);
+
+    /**
+     * The index of the first element that should not be read.
+     * @return an <code>int</code> value >= getIndex() 
+     */
+    int putIndex();
+    
+    /**
+     * Reset the current getIndex to the mark 
+     */
+    void reset();
+    
+    /**
+     * Set the buffers start getIndex.
+     * @param newStart an <code>int</code> value
+     */
+    void setGetIndex(int newStart);
+    
+    /**
+     * Set a specific value for the mark.
+     * @param newMark an <code>int</code> value
+     */
+    void setMarkIndex(int newMark);
+    
+    /**
+     * 
+     * @param newLimit an <code>int</code> value
+     */
+    void setPutIndex(int newLimit);
+    
+    /**
+     * Skip _content. The getIndex is updated by min(remaining(), n)
+     * @param n The number of bytes to skip
+     * @return the number of bytes skipped.
+     */
+    int skip(int n);
+
+    /**
+     * 
+     * @return a volitile <code>Buffer</code> from the postion to the putIndex.
+     */
+    Buffer slice();
+    
+    /**
+     * 
+     *
+     * @return a volitile <code>Buffer</code> value from the mark to the putIndex
+     */
+    Buffer sliceFromMark();
+    
+    /**
+     * 
+     *
+     * @param length an <code>int</code> value
+     * @return a valitile <code>Buffer</code> value from the mark of the length requested.
+     */
+    Buffer sliceFromMark(int length);
+    
+    /**
+     * 
+     * @return a <code>String</code> value describing the state and contents of the buffer.
+     */
+    String toDetailString();
+
+    /* ------------------------------------------------------------ */
+    /** Write the buffer's contents to the output stream
+     * @param out
+     */
+    void writeTo(OutputStream out) throws IOException;
+
+    /* ------------------------------------------------------------ */
+    /** Read the buffer's contents from the input stream
+     * @param in input stream
+     * @param max maximum number of bytes that may be read
+     * @return actual number of bytes read or -1 for EOF
+     */
+    int readFrom(InputStream in, int max) throws IOException;
+    
+
+    /* ------------------------------------------------------------ */
+    String toString(String charset);
+    
+    /* ------------------------------------------------------------ */
+    String toString(Charset charset);
+
+    /*
+     * Buffers implementing this interface should be compared with case insensitive equals
+     *
+     */
+    public interface CaseInsensitve
+    {}
+
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/BufferCache.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,169 @@
+//
+//  ========================================================================
+//  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.io;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map.Entry;
+
+import org.eclipse.jetty.util.StringMap;
+
+/* ------------------------------------------------------------------------------- */
+/** 
+ * Stores a collection of {@link Buffer} objects.
+ * Buffers are stored in an ordered collection and can retreived by index or value
+ * 
+ */
+public class BufferCache
+{
+    private final HashMap _bufferMap=new HashMap();
+    private final StringMap _stringMap=new StringMap(StringMap.CASE_INSENSTIVE);
+    private final ArrayList _index= new ArrayList();
+
+    /* ------------------------------------------------------------------------------- */
+    /** Add a buffer to the cache at the specified index.
+     * @param value The content of the buffer.
+     */
+    public CachedBuffer add(String value, int ordinal)
+    {
+        CachedBuffer buffer= new CachedBuffer(value, ordinal);
+        _bufferMap.put(buffer, buffer);
+        _stringMap.put(value, buffer);
+        while ((ordinal - _index.size()) >= 0)
+            _index.add(null);
+        if (_index.get(ordinal)==null)
+            _index.add(ordinal, buffer);
+        return buffer;
+    }
+
+    public CachedBuffer get(int ordinal)
+    {
+        if (ordinal < 0 || ordinal >= _index.size())
+            return null;
+        return (CachedBuffer)_index.get(ordinal);
+    }
+
+    public CachedBuffer get(Buffer buffer)
+    {
+        return (CachedBuffer)_bufferMap.get(buffer);
+    }
+
+    public CachedBuffer get(String value)
+    {
+        return (CachedBuffer)_stringMap.get(value);
+    }
+
+    public Buffer lookup(Buffer buffer)
+    {
+        if (buffer instanceof CachedBuffer)
+            return buffer;
+        
+        Buffer b= get(buffer);
+        if (b == null)
+        {
+            if (buffer instanceof Buffer.CaseInsensitve)
+                return buffer;
+            return new ByteArrayBuffer.CaseInsensitive(buffer.asArray(),0,buffer.length(),Buffer.IMMUTABLE);
+        }
+
+        return b;
+    }
+    
+    public CachedBuffer getBest(byte[] value, int offset, int maxLength)
+    {
+        Entry entry = _stringMap.getBestEntry(value, offset, maxLength);
+        if (entry!=null)
+            return (CachedBuffer)entry.getValue();
+        return null;
+    }
+
+    public Buffer lookup(String value)
+    {
+        Buffer b= get(value);
+        if (b == null)
+        {
+            return new CachedBuffer(value,-1);
+        }
+        return b;
+    }
+
+    public String toString(Buffer buffer)
+    {
+        return lookup(buffer).toString();
+    }
+
+    public int getOrdinal(String value)
+    {
+        CachedBuffer buffer = (CachedBuffer)_stringMap.get(value);
+        return buffer==null?-1:buffer.getOrdinal();
+    }
+    
+    public int getOrdinal(Buffer buffer)
+    {
+        if (buffer instanceof CachedBuffer)
+            return ((CachedBuffer)buffer).getOrdinal();
+        buffer=lookup(buffer);
+        if (buffer!=null && buffer instanceof CachedBuffer)
+            return ((CachedBuffer)buffer).getOrdinal();
+        return -1;
+    }
+    
+    public static class CachedBuffer extends ByteArrayBuffer.CaseInsensitive
+    {
+        private final int _ordinal;
+        private HashMap _associateMap=null;
+        
+        public CachedBuffer(String value, int ordinal)
+        {
+            super(value);
+            _ordinal= ordinal;
+        }
+
+        public int getOrdinal()
+        {
+            return _ordinal;
+        }
+
+        public CachedBuffer getAssociate(Object key)
+        {
+            if (_associateMap==null)
+                return null;
+            return (CachedBuffer)_associateMap.get(key);
+        }
+
+        // TODO Replace Associate with a mime encoding specific solution
+        public void setAssociate(Object key, CachedBuffer associate)
+        {
+            if (_associateMap==null)
+                _associateMap=new HashMap();
+            _associateMap.put(key,associate);
+        }
+    }
+    
+    
+    @Override
+    public String toString()
+    {
+        return "CACHE["+
+        	"bufferMap="+_bufferMap+
+        	",stringMap="+_stringMap+
+        	",index="+_index+
+        	"]";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/BufferDateCache.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,61 @@
+//
+//  ========================================================================
+//  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.io;
+
+import java.text.DateFormatSymbols;
+import java.util.Locale;
+
+import org.eclipse.jetty.util.DateCache;
+
+public class BufferDateCache extends DateCache
+{
+    Buffer _buffer;
+    String _last;
+    
+    public BufferDateCache()
+    {
+        super();
+    }
+
+    public BufferDateCache(String format, DateFormatSymbols s)
+    {
+        super(format,s);
+    }
+
+    public BufferDateCache(String format, Locale l)
+    {
+        super(format,l);
+    }
+
+    public BufferDateCache(String format)
+    {
+        super(format);
+    }
+
+    public synchronized Buffer formatBuffer(long date)
+    {
+        String d = super.format(date);
+        if (d==_last)
+            return _buffer;
+        _last=d;
+        _buffer=new ByteArrayBuffer(d);
+        
+        return _buffer;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/BufferUtil.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,359 @@
+//
+//  ========================================================================
+//  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.io;
+
+import org.eclipse.jetty.io.BufferCache.CachedBuffer;
+import org.eclipse.jetty.util.StringUtil;
+
+/* ------------------------------------------------------------------------------- */
+/** Buffer utility methods.
+ * 
+ * 
+ */
+public class BufferUtil
+{
+    static final byte SPACE= 0x20;
+    static final byte MINUS= '-';
+    static final byte[] DIGIT=
+    {(byte)'0',(byte)'1',(byte)'2',(byte)'3',(byte)'4',(byte)'5',(byte)'6',(byte)'7',(byte)'8',(byte)'9',(byte)'A',(byte)'B',(byte)'C',(byte)'D',(byte)'E',(byte)'F'};
+
+    /**
+     * Convert buffer to an integer.
+     * Parses up to the first non-numeric character. If no number is found an
+     * IllegalArgumentException is thrown
+     * @param buffer A buffer containing an integer. The position is not changed.
+     * @return an int 
+     */
+    public static int toInt(Buffer buffer)
+    {
+        int val= 0;
+        boolean started= false;
+        boolean minus= false;
+        for (int i= buffer.getIndex(); i < buffer.putIndex(); i++)
+        {
+            byte b= buffer.peek(i);
+            if (b <= SPACE)
+            {
+                if (started)
+                    break;
+            }
+            else if (b >= '0' && b <= '9')
+            {
+                val= val * 10 + (b - '0');
+                started= true;
+            }
+            else if (b == MINUS && !started)
+            {
+                minus= true;
+            }
+            else
+                break;
+        }
+
+        if (started)
+            return minus ? (-val) : val;
+        throw new NumberFormatException(buffer.toString());
+    }
+    
+    /**
+     * Convert buffer to an long.
+     * Parses up to the first non-numeric character. If no number is found an
+     * IllegalArgumentException is thrown
+     * @param buffer A buffer containing an integer. The position is not changed.
+     * @return an int 
+     */
+    public static long toLong(Buffer buffer)
+    {
+        long val= 0;
+        boolean started= false;
+        boolean minus= false;
+        for (int i= buffer.getIndex(); i < buffer.putIndex(); i++)
+        {
+            byte b= buffer.peek(i);
+            if (b <= SPACE)
+            {
+                if (started)
+                    break;
+            }
+            else if (b >= '0' && b <= '9')
+            {
+                val= val * 10L + (b - '0');
+                started= true;
+            }
+            else if (b == MINUS && !started)
+            {
+                minus= true;
+            }
+            else
+                break;
+        }
+
+        if (started)
+            return minus ? (-val) : val;
+        throw new NumberFormatException(buffer.toString());
+    }
+
+    public static void putHexInt(Buffer buffer, int n)
+    {
+
+        if (n < 0)
+        {
+            buffer.put((byte)'-');
+
+            if (n == Integer.MIN_VALUE)
+            {
+                buffer.put((byte)(0x7f&'8'));
+                buffer.put((byte)(0x7f&'0'));
+                buffer.put((byte)(0x7f&'0'));
+                buffer.put((byte)(0x7f&'0'));
+                buffer.put((byte)(0x7f&'0'));
+                buffer.put((byte)(0x7f&'0'));
+                buffer.put((byte)(0x7f&'0'));
+                buffer.put((byte)(0x7f&'0'));
+                
+                return;
+            }
+            n= -n;
+        }
+
+        if (n < 0x10)
+        {
+            buffer.put(DIGIT[n]);
+        }
+        else
+        {
+            boolean started= false;
+            // This assumes constant time int arithmatic
+            for (int i= 0; i < hexDivisors.length; i++)
+            {
+                if (n < hexDivisors[i])
+                {
+                    if (started)
+                        buffer.put((byte)'0');
+                    continue;
+                }
+
+                started= true;
+                int d= n / hexDivisors[i];
+                buffer.put(DIGIT[d]);
+                n= n - d * hexDivisors[i];
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Add hex integer BEFORE current getIndex.
+     * @param buffer
+     * @param n
+     */
+    public static void prependHexInt(Buffer buffer, int n)
+    {
+        if (n==0)
+        {
+            int gi=buffer.getIndex();
+            buffer.poke(--gi,(byte)'0');
+            buffer.setGetIndex(gi);
+        }
+        else
+        {
+            boolean minus=false;
+            if (n<0)
+            {
+                minus=true;
+                n=-n;
+            }
+
+            int gi=buffer.getIndex();
+            while(n>0)
+            {
+                int d = 0xf&n;
+                n=n>>4;
+                buffer.poke(--gi,DIGIT[d]);
+            }
+            
+            if (minus)
+                buffer.poke(--gi,(byte)'-');
+            buffer.setGetIndex(gi);
+        }
+    }
+    
+
+    /* ------------------------------------------------------------ */
+    public static void putDecInt(Buffer buffer, int n)
+    {
+        if (n < 0)
+        {
+            buffer.put((byte)'-');
+
+            if (n == Integer.MIN_VALUE)
+            {
+                buffer.put((byte)'2');
+                n= 147483648;
+            }
+            else
+                n= -n;
+        }
+
+        if (n < 10)
+        {
+            buffer.put(DIGIT[n]);
+        }
+        else
+        {
+            boolean started= false;
+            // This assumes constant time int arithmatic
+            for (int i= 0; i < decDivisors.length; i++)
+            {
+                if (n < decDivisors[i])
+                {
+                    if (started)
+                        buffer.put((byte)'0');
+                    continue;
+                }
+
+                started= true;
+                int d= n / decDivisors[i];
+                buffer.put(DIGIT[d]);
+                n= n - d * decDivisors[i];
+            }
+        }
+    }
+    
+    public static void putDecLong(Buffer buffer, long n)
+    {
+        if (n < 0)
+        {
+            buffer.put((byte)'-');
+
+            if (n == Long.MIN_VALUE)
+            {
+                buffer.put((byte)'9');
+                n= 223372036854775808L;
+            }
+            else
+                n= -n;
+        }
+
+        if (n < 10)
+        {
+            buffer.put(DIGIT[(int)n]);
+        }
+        else
+        {
+            boolean started= false;
+            // This assumes constant time int arithmatic
+            for (int i= 0; i < decDivisorsL.length; i++)
+            {
+                if (n < decDivisorsL[i])
+                {
+                    if (started)
+                        buffer.put((byte)'0');
+                    continue;
+                }
+
+                started= true;
+                long d= n / decDivisorsL[i];
+                buffer.put(DIGIT[(int)d]);
+                n= n - d * decDivisorsL[i];
+            }
+        }
+    }
+    
+    public static Buffer toBuffer(long value)
+    {
+        ByteArrayBuffer buf=new ByteArrayBuffer(32);
+        putDecLong(buf, value);
+        return buf;
+    }
+
+    private final static int[] decDivisors=
+    { 
+        1000000000,
+        100000000,
+        10000000,
+        1000000,
+        100000,
+        10000,
+        1000,
+        100,
+        10,
+        1 
+    };
+
+    private final static int[] hexDivisors=
+    {
+        0x10000000,
+        0x1000000, 
+        0x100000, 
+        0x10000, 
+        0x1000, 
+        0x100, 
+        0x10, 
+        0x1 
+    };
+
+    private final static long[] decDivisorsL=
+    { 
+        1000000000000000000L,
+        100000000000000000L,
+        10000000000000000L,
+        1000000000000000L,
+        100000000000000L,
+        10000000000000L,
+        1000000000000L,
+        100000000000L,
+        10000000000L,
+        1000000000L,
+        100000000L,
+        10000000L,
+        1000000L,
+        100000L,
+        10000L,
+        1000L,
+        100L,
+        10L,
+        1L 
+    };
+
+
+    public static void putCRLF(Buffer buffer)
+    {
+        buffer.put((byte)13);
+        buffer.put((byte)10);
+    }
+    
+    public static boolean isPrefix(Buffer prefix,Buffer buffer)
+    {
+        if (prefix.length()>buffer.length())
+            return false;
+        int bi=buffer.getIndex();
+        for (int i=prefix.getIndex(); i<prefix.putIndex();i++)
+            if (prefix.peek(i)!=buffer.peek(bi++))
+                return false;
+        return true;
+    }
+
+    public static String to8859_1_String(Buffer buffer)
+    {
+        if (buffer instanceof CachedBuffer)
+            return buffer.toString();
+        return buffer.toString(StringUtil.__ISO_8859_1_CHARSET);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/Buffers.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,38 @@
+//
+//  ========================================================================
+//  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.io;
+
+
+/* ------------------------------------------------------------ */
+/** BufferSource.
+ * Represents a pool or other source of buffers and abstracts the creation
+ * of specific types of buffers (eg NIO).   The concept of big and little buffers
+ * is supported, but these terms have no absolute meaning and must be determined by context.
+ * 
+ */
+public interface Buffers
+{
+    enum Type { BYTE_ARRAY, DIRECT, INDIRECT } ;
+    
+    Buffer getHeader();
+    Buffer getBuffer();
+    Buffer getBuffer(int size);
+    
+    void returnBuffer(Buffer buffer);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/BuffersFactory.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,29 @@
+//
+//  ========================================================================
+//  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.io;
+
+public class BuffersFactory
+{
+    public static Buffers newBuffers(Buffers.Type headerType, int headerSize, Buffers.Type bufferType, int bufferSize, Buffers.Type otherType,int maxSize)
+    {
+        if (maxSize>=0)
+            return new PooledBuffers(headerType,headerSize,bufferType,bufferSize,otherType,maxSize);
+        return new ThreadLocalBuffers(headerType,headerSize,bufferType,bufferSize,otherType);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/ByteArrayBuffer.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,439 @@
+//
+//  ========================================================================
+//  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.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+
+import org.eclipse.jetty.util.StringUtil;
+
+/* ------------------------------------------------------------------------------- */
+/**
+ * 
+ */
+public class ByteArrayBuffer extends AbstractBuffer
+{
+    // Set a maximum size to a write for the writeTo method, to ensure that very large content is not
+    // written as a single write (which may fall foul to write timeouts if consumed slowly).
+    final static int MAX_WRITE=Integer.getInteger("org.eclipse.jetty.io.ByteArrayBuffer.MAX_WRITE",128*1024);
+    final protected byte[] _bytes;
+
+    protected ByteArrayBuffer(int size, int access, boolean isVolatile)
+    {
+        this(new byte[size],0,0,access, isVolatile);
+    }
+    
+    public ByteArrayBuffer(byte[] bytes)
+    {
+        this(bytes, 0, bytes.length, READWRITE);
+    }
+
+    public ByteArrayBuffer(byte[] bytes, int index, int length)
+    {
+        this(bytes, index, length, READWRITE);
+    }
+
+    public ByteArrayBuffer(byte[] bytes, int index, int length, int access)
+    {
+        super(READWRITE, NON_VOLATILE);
+        _bytes = bytes;
+        setPutIndex(index + length);
+        setGetIndex(index);
+        _access = access;
+    }
+
+    public ByteArrayBuffer(byte[] bytes, int index, int length, int access, boolean isVolatile)
+    {
+        super(READWRITE, isVolatile);
+        _bytes = bytes;
+        setPutIndex(index + length);
+        setGetIndex(index);
+        _access = access;
+    }
+
+    public ByteArrayBuffer(int size)
+    {
+        this(new byte[size], 0, 0, READWRITE);
+        setPutIndex(0);
+    }
+
+    public ByteArrayBuffer(String value)
+    {
+        super(READWRITE,NON_VOLATILE);
+        _bytes = StringUtil.getBytes(value);
+        setGetIndex(0);
+        setPutIndex(_bytes.length);
+        _access=IMMUTABLE;
+        _string = value;
+    }
+    
+    public ByteArrayBuffer(String value,boolean immutable)
+    {
+        super(READWRITE,NON_VOLATILE);
+        _bytes = StringUtil.getBytes(value);
+        setGetIndex(0);
+        setPutIndex(_bytes.length);
+        if (immutable)
+        {
+            _access=IMMUTABLE;
+            _string = value;
+        }
+    }
+
+    public ByteArrayBuffer(String value,String encoding) throws UnsupportedEncodingException
+    {
+        super(READWRITE,NON_VOLATILE);
+        _bytes = value.getBytes(encoding);
+        setGetIndex(0);
+        setPutIndex(_bytes.length);
+        _access=IMMUTABLE;
+        _string = value;
+    }
+
+    public byte[] array()
+    {
+        return _bytes;
+    }
+
+    public int capacity()
+    {
+        return _bytes.length;
+    }
+    
+    @Override
+    public void compact()
+    {
+        if (isReadOnly()) 
+            throw new IllegalStateException(__READONLY);
+        int s = markIndex() >= 0 ? markIndex() : getIndex();
+        if (s > 0)
+        {
+            int length = putIndex() - s;
+            if (length > 0)
+            {
+                System.arraycopy(_bytes, s,_bytes, 0, length);
+            }
+            if (markIndex() > 0) setMarkIndex(markIndex() - s);
+            setGetIndex(getIndex() - s);
+            setPutIndex(putIndex() - s);
+        }
+    }
+
+
+    @Override
+    public boolean equals(Object obj)
+    {
+        if (obj==this)
+            return true;
+
+        if (obj == null || !(obj instanceof Buffer)) 
+            return false;
+        
+        if (obj instanceof Buffer.CaseInsensitve)
+            return equalsIgnoreCase((Buffer)obj);
+        
+
+        Buffer b = (Buffer) obj;
+        
+        // reject different lengths
+        if (b.length() != length()) 
+            return false;
+
+        // reject AbstractBuffer with different hash value
+        if (_hash != 0 && obj instanceof AbstractBuffer)
+        {
+            AbstractBuffer ab = (AbstractBuffer) obj;
+            if (ab._hash != 0 && _hash != ab._hash) 
+                return false;
+        }
+
+        // Nothing for it but to do the hard grind.
+        int get=getIndex();
+        int bi=b.putIndex();
+        for (int i = putIndex(); i-->get;)
+        {
+            byte b1 = _bytes[i];
+            byte b2 = b.peek(--bi);
+            if (b1 != b2) return false;
+        }
+        return true;
+    }
+
+
+    @Override
+    public boolean equalsIgnoreCase(Buffer b)
+    {
+        if (b==this)
+            return true;
+        
+        // reject different lengths
+        if (b==null || b.length() != length()) 
+            return false;
+
+        // reject AbstractBuffer with different hash value
+        if (_hash != 0 && b instanceof AbstractBuffer)
+        {
+            AbstractBuffer ab = (AbstractBuffer) b;
+            if (ab._hash != 0 && _hash != ab._hash) return false;
+        }
+
+        // Nothing for it but to do the hard grind.
+        int get=getIndex();
+        int bi=b.putIndex();
+        byte[] barray=b.array();
+        if (barray==null)
+        {
+            for (int i = putIndex(); i-->get;)
+            {
+                byte b1 = _bytes[i];
+                byte b2 = b.peek(--bi);
+                if (b1 != b2)
+                {
+                    if ('a' <= b1 && b1 <= 'z') b1 = (byte) (b1 - 'a' + 'A');
+                    if ('a' <= b2 && b2 <= 'z') b2 = (byte) (b2 - 'a' + 'A');
+                    if (b1 != b2) return false;
+                }
+            }
+        }
+        else
+        {
+            for (int i = putIndex(); i-->get;)
+            {
+                byte b1 = _bytes[i];
+                byte b2 = barray[--bi];
+                if (b1 != b2)
+                {
+                    if ('a' <= b1 && b1 <= 'z') b1 = (byte) (b1 - 'a' + 'A');
+                    if ('a' <= b2 && b2 <= 'z') b2 = (byte) (b2 - 'a' + 'A');
+                    if (b1 != b2) return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public byte get()
+    {
+        return _bytes[_get++];
+    }
+
+    @Override
+    public int hashCode()
+    {
+        if (_hash == 0 || _hashGet!=_get || _hashPut!=_put) 
+        {
+            int get=getIndex();
+            for (int i = putIndex(); i-- >get;)
+            {
+                byte b = _bytes[i];
+                if ('a' <= b && b <= 'z') 
+                    b = (byte) (b - 'a' + 'A');
+                _hash = 31 * _hash + b;
+            }
+            if (_hash == 0) 
+                _hash = -1;
+            _hashGet=_get;
+            _hashPut=_put;
+        }
+        return _hash;
+    }
+    
+    
+    public byte peek(int index)
+    {
+        return _bytes[index];
+    }
+    
+    public int peek(int index, byte[] b, int offset, int length)
+    {
+        int l = length;
+        if (index + l > capacity())
+        {
+            l = capacity() - index;
+            if (l==0)
+                return -1;
+        }
+        
+        if (l < 0) 
+            return -1;
+        
+        System.arraycopy(_bytes, index, b, offset, l);
+        return l;
+    }
+
+    public void poke(int index, byte b)
+    {
+        /* 
+        if (isReadOnly()) 
+            throw new IllegalStateException(__READONLY);
+        
+        if (index < 0) 
+            throw new IllegalArgumentException("index<0: " + index + "<0");
+        if (index > capacity())
+                throw new IllegalArgumentException("index>capacity(): " + index + ">" + capacity());
+        */
+        _bytes[index] = b;
+    }
+    
+    @Override
+    public int poke(int index, Buffer src)
+    {
+        _hash=0;
+        
+        /* 
+        if (isReadOnly()) 
+            throw new IllegalStateException(__READONLY);
+        if (index < 0) 
+            throw new IllegalArgumentException("index<0: " + index + "<0");
+        */
+        
+        int length=src.length();
+        if (index + length > capacity())
+        {
+            length=capacity()-index;
+            /*
+            if (length<0)
+                throw new IllegalArgumentException("index>capacity(): " + index + ">" + capacity());
+            */
+        }
+        
+        byte[] src_array = src.array();
+        if (src_array != null)
+            System.arraycopy(src_array, src.getIndex(), _bytes, index, length);
+        else 
+        {
+            int s=src.getIndex();
+            for (int i=0;i<length;i++)
+                _bytes[index++]=src.peek(s++);
+        }
+        
+        return length;
+    }
+    
+
+    @Override
+    public int poke(int index, byte[] b, int offset, int length)
+    {
+        _hash=0;
+        /*
+        if (isReadOnly()) 
+            throw new IllegalStateException(__READONLY);
+        if (index < 0) 
+            throw new IllegalArgumentException("index<0: " + index + "<0");
+        */
+        
+        if (index + length > capacity())
+        {
+            length=capacity()-index;
+            /* if (length<0)
+                throw new IllegalArgumentException("index>capacity(): " + index + ">" + capacity());
+            */
+        }
+        
+        System.arraycopy(b, offset, _bytes, index, length);
+        
+        return length;
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public void writeTo(OutputStream out)
+        throws IOException
+    {
+        int len=length();
+        if (MAX_WRITE>0 && len>MAX_WRITE)
+        {
+            int off=getIndex();
+            while(len>0)
+            {
+                int c=len>MAX_WRITE?MAX_WRITE:len;
+                out.write(_bytes,off,c);
+                off+=c;
+                len-=c;
+            }
+        }
+        else
+            out.write(_bytes,getIndex(),len);
+        if (!isImmutable())
+            clear();
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public int readFrom(InputStream in,int max) throws IOException
+    {
+        if (max<0||max>space())
+            max=space();
+        int p = putIndex();
+        
+        int len=0, total=0, available=max;
+        while (total<max) 
+        {
+            len=in.read(_bytes,p,available);
+            if (len<0)
+                break;
+            else if (len>0)
+            {
+                p += len;
+                total += len;
+                available -= len;
+                setPutIndex(p);
+            }
+            if (in.available()<=0)
+                break;
+        }
+        if (len<0 && total==0)
+            return -1;
+        return total;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public int space()
+    {
+        return _bytes.length - _put;
+    }
+
+    
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    public static class CaseInsensitive extends ByteArrayBuffer implements Buffer.CaseInsensitve
+    {
+        public CaseInsensitive(String s)
+        {
+            super(s);
+        }
+        
+        public CaseInsensitive(byte[] b, int o, int l, int rw)
+        {
+            super(b,o,l,rw);
+        }
+
+        @Override
+        public boolean equals(Object obj)
+        {
+            return obj instanceof Buffer && equalsIgnoreCase((Buffer)obj);
+        }
+        
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/ByteArrayEndPoint.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,408 @@
+//
+//  ========================================================================
+//  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.io;
+
+import java.io.IOException;
+
+
+
+/* ------------------------------------------------------------ */
+/** ByteArrayEndPoint.
+ *
+ *
+ */
+public class ByteArrayEndPoint implements ConnectedEndPoint
+{
+    protected byte[] _inBytes;
+    protected ByteArrayBuffer _in;
+    protected ByteArrayBuffer _out;
+    protected boolean _closed;
+    protected boolean _nonBlocking;
+    protected boolean _growOutput;
+    protected Connection _connection;
+    protected int _maxIdleTime;
+
+
+    /* ------------------------------------------------------------ */
+    /**
+     *
+     */
+    public ByteArrayEndPoint()
+    {
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.io.ConnectedEndPoint#getConnection()
+     */
+    public Connection getConnection()
+    {
+        return _connection;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.io.ConnectedEndPoint#setConnection(org.eclipse.jetty.io.Connection)
+     */
+    public void setConnection(Connection connection)
+    {
+        _connection=connection;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return the nonBlocking
+     */
+    public boolean isNonBlocking()
+    {
+        return _nonBlocking;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param nonBlocking the nonBlocking to set
+     */
+    public void setNonBlocking(boolean nonBlocking)
+    {
+        _nonBlocking=nonBlocking;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     *
+     */
+    public ByteArrayEndPoint(byte[] input, int outputSize)
+    {
+        _inBytes=input;
+        _in=new ByteArrayBuffer(input);
+        _out=new ByteArrayBuffer(outputSize);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the in.
+     */
+    public ByteArrayBuffer getIn()
+    {
+        return _in;
+    }
+    /* ------------------------------------------------------------ */
+    /**
+     * @param in The in to set.
+     */
+    public void setIn(ByteArrayBuffer in)
+    {
+        _in = in;
+    }
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the out.
+     */
+    public ByteArrayBuffer getOut()
+    {
+        return _out;
+    }
+    /* ------------------------------------------------------------ */
+    /**
+     * @param out The out to set.
+     */
+    public void setOut(ByteArrayBuffer out)
+    {
+        _out = out;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.io.EndPoint#isOpen()
+     */
+    public boolean isOpen()
+    {
+        return !_closed;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     *  @see org.eclipse.jetty.io.EndPoint#isInputShutdown()
+     */
+    public boolean isInputShutdown()
+    {
+        return _closed;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     *  @see org.eclipse.jetty.io.EndPoint#isOutputShutdown()
+     */
+    public boolean isOutputShutdown()
+    {
+        return _closed;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.io.EndPoint#isBlocking()
+     */
+    public boolean isBlocking()
+    {
+        return !_nonBlocking;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean blockReadable(long millisecs)
+    {
+        return true;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean blockWritable(long millisecs)
+    {
+        return true;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.io.EndPoint#shutdownOutput()
+     */
+    public void shutdownOutput() throws IOException
+    {
+        close();
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.io.EndPoint#shutdownInput()
+     */
+    public void shutdownInput() throws IOException
+    {
+        close();
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.io.EndPoint#close()
+     */
+    public void close() throws IOException
+    {
+        _closed=true;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.io.EndPoint#fill(org.eclipse.io.Buffer)
+     */
+    public int fill(Buffer buffer) throws IOException
+    {
+        if (_closed)
+            throw new IOException("CLOSED");
+
+        if (_in!=null && _in.length()>0)
+        {
+            int len = buffer.put(_in);
+            _in.skip(len);
+            return len;
+        }
+
+        if (_in!=null && _in.length()==0 && _nonBlocking)
+            return 0;
+
+        close();
+        return -1;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.io.EndPoint#flush(org.eclipse.io.Buffer)
+     */
+    public int flush(Buffer buffer) throws IOException
+    {
+        if (_closed)
+            throw new IOException("CLOSED");
+        if (_growOutput && buffer.length()>_out.space())
+        {
+            _out.compact();
+
+            if (buffer.length()>_out.space())
+            {
+                ByteArrayBuffer n = new ByteArrayBuffer(_out.putIndex()+buffer.length());
+
+                n.put(_out.peek(0,_out.putIndex()));
+                if (_out.getIndex()>0)
+                {
+                    n.mark();
+                    n.setGetIndex(_out.getIndex());
+                }
+                _out=n;
+            }
+        }
+        int len = _out.put(buffer);
+        if (!buffer.isImmutable())
+            buffer.skip(len);
+        return len;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.io.EndPoint#flush(org.eclipse.io.Buffer, org.eclipse.io.Buffer, org.eclipse.io.Buffer)
+     */
+    public int flush(Buffer header, Buffer buffer, Buffer trailer) throws IOException
+    {
+        if (_closed)
+            throw new IOException("CLOSED");
+
+        int flushed=0;
+
+        if (header!=null && header.length()>0)
+            flushed=flush(header);
+
+        if (header==null || header.length()==0)
+        {
+            if (buffer!=null && buffer.length()>0)
+                flushed+=flush(buffer);
+
+            if (buffer==null || buffer.length()==0)
+            {
+                if (trailer!=null && trailer.length()>0)
+                {
+                    flushed+=flush(trailer);
+                }
+            }
+        }
+
+        return flushed;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     *
+     */
+    public void reset()
+    {
+        _closed=false;
+        _in.clear();
+        _out.clear();
+        if (_inBytes!=null)
+            _in.setPutIndex(_inBytes.length);
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.io.EndPoint#getLocalAddr()
+     */
+    public String getLocalAddr()
+    {
+        return null;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.io.EndPoint#getLocalHost()
+     */
+    public String getLocalHost()
+    {
+        return null;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.io.EndPoint#getLocalPort()
+     */
+    public int getLocalPort()
+    {
+        return 0;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.io.EndPoint#getRemoteAddr()
+     */
+    public String getRemoteAddr()
+    {
+        return null;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.io.EndPoint#getRemoteHost()
+     */
+    public String getRemoteHost()
+    {
+        return null;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.io.EndPoint#getRemotePort()
+     */
+    public int getRemotePort()
+    {
+        return 0;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.io.EndPoint#getConnection()
+     */
+    public Object getTransport()
+    {
+        return _inBytes;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void flush() throws IOException
+    {
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @return the growOutput
+     */
+    public boolean isGrowOutput()
+    {
+        return _growOutput;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param growOutput the growOutput to set
+     */
+    public void setGrowOutput(boolean growOutput)
+    {
+        _growOutput=growOutput;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.io.EndPoint#getMaxIdleTime()
+     */
+    public int getMaxIdleTime()
+    {
+        return _maxIdleTime;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.io.EndPoint#setMaxIdleTime(int)
+     */
+    public void setMaxIdleTime(int timeMs) throws IOException
+    {
+        _maxIdleTime=timeMs;
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/ConnectedEndPoint.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,25 @@
+//
+//  ========================================================================
+//  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.io;
+
+public interface ConnectedEndPoint extends EndPoint
+{
+    Connection getConnection();
+    void setConnection(Connection connection);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/Connection.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,77 @@
+//
+//  ========================================================================
+//  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.io;
+
+import java.io.IOException;
+
+/* ------------------------------------------------------------ */
+/** Abstract Connection used by Jetty Connectors.
+ * <p>
+ * Jetty will call the handle method of a connection when there is work
+ * to be done on the connection.  For blocking connections, this is soon
+ * as the connection is open and handle will keep being called until the
+ * connection is closed.   For non-blocking connections, handle will only
+ * be called if there are bytes to be read or the connection becomes writable
+ * after being write blocked.
+ *
+ * @see org.eclipse.jetty.io.nio.SelectorManager
+ */
+public interface Connection
+{
+    /* ------------------------------------------------------------ */
+    /**
+     * Handle the connection.
+     * @return The Connection to use for the next handling of the connection.
+     * This allows protocol upgrades and support for CONNECT.
+     * @throws IOException if the handling of I/O operations fail
+     */
+    Connection handle() throws IOException;
+
+    /**
+     * @return the timestamp at which the connection was created
+     */
+    long getTimeStamp();
+
+    /**
+     * @return whether this connection is idle, that is not parsing and not generating
+     * @see #onIdleExpired(long)
+     */
+    boolean isIdle();
+
+    /**
+     * <p>The semantic of this method is to return true to indicate interest in further reads,
+     * or false otherwise, but it is misnamed and should be really called <code>isReadInterested()</code>.</p>
+     *
+     * @return true to indicate interest in further reads, false otherwise
+     */
+    // TODO: rename to isReadInterested() in the next release
+    boolean isSuspended();
+
+    /**
+     * Called after the connection is closed
+     */
+    void onClose();
+
+    /**
+     * Called when the connection idle timeout expires
+     * @param idleForMs how long the connection has been idle
+     * @see #isIdle()
+     */
+    void onIdleExpired(long idleForMs);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/EndPoint.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,175 @@
+//
+//  ========================================================================
+//  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.io;
+
+import java.io.IOException;
+
+
+/**
+ *
+ * A transport EndPoint
+ */
+public interface EndPoint
+{
+    /**
+     * Shutdown any backing output stream associated with the endpoint
+     */
+    void shutdownOutput() throws IOException;
+
+    boolean isOutputShutdown();
+
+    /**
+     * Shutdown any backing input stream associated with the endpoint
+     */
+    void shutdownInput() throws IOException;
+
+    boolean isInputShutdown();
+
+    /**
+     * Close any backing stream associated with the endpoint
+     */
+    void close() throws IOException;
+
+    /**
+     * Fill the buffer from the current putIndex to it's capacity from whatever
+     * byte source is backing the buffer. The putIndex is increased if bytes filled.
+     * The buffer may chose to do a compact before filling.
+     * @return an <code>int</code> value indicating the number of bytes
+     * filled or -1 if EOF is reached.
+     * @throws EofException If input is shutdown or the endpoint is closed.
+     */
+    int fill(Buffer buffer) throws IOException;
+
+
+    /**
+     * Flush the buffer from the current getIndex to it's putIndex using whatever byte
+     * sink is backing the buffer. The getIndex is updated with the number of bytes flushed.
+     * Any mark set is cleared.
+     * If the entire contents of the buffer are flushed, then an implicit empty() is done.
+     *
+     * @param buffer The buffer to flush. This buffers getIndex is updated.
+     * @return  the number of bytes written
+     * @throws EofException If the endpoint is closed or output is shutdown.
+     */
+    int flush(Buffer buffer) throws IOException;
+
+    /**
+     * Flush the buffer from the current getIndex to it's putIndex using whatever byte
+     * sink is backing the buffer. The getIndex is updated with the number of bytes flushed.
+     * Any mark set is cleared.
+     * If the entire contents of the buffer are flushed, then an implicit empty() is done.
+     * The passed header/trailer buffers are written before/after the contents of this buffer. This may be done
+     * either as gather writes, as a poke into this buffer or as several writes. The implementation is free to
+     * select the optimal mechanism.
+     * @param header A buffer to write before flushing this buffer. This buffers getIndex is updated.
+     * @param buffer The buffer to flush. This buffers getIndex is updated.
+     * @param trailer A buffer to write after flushing this buffer. This buffers getIndex is updated.
+     * @return the total number of bytes written.
+     */
+    int flush(Buffer header, Buffer buffer, Buffer trailer) throws IOException;
+
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The local IP address to which this <code>EndPoint</code> is bound, or <code>null</code>
+     * if this <code>EndPoint</code> does not represent a network connection.
+     */
+    public String getLocalAddr();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The local host name to which this <code>EndPoint</code> is bound, or <code>null</code>
+     * if this <code>EndPoint</code> does not represent a network connection.
+     */
+    public String getLocalHost();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The local port number on which this <code>EndPoint</code> is listening, or <code>0</code>
+     * if this <code>EndPoint</code> does not represent a network connection.
+     */
+    public int getLocalPort();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The remote IP address to which this <code>EndPoint</code> is connected, or <code>null</code>
+     * if this <code>EndPoint</code> does not represent a network connection.
+     */
+    public String getRemoteAddr();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The host name of the remote machine to which this <code>EndPoint</code> is connected, or <code>null</code>
+     * if this <code>EndPoint</code> does not represent a network connection.
+     */
+    public String getRemoteHost();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The remote port number to which this <code>EndPoint</code> is connected, or <code>0</code>
+     * if this <code>EndPoint</code> does not represent a network connection.
+     */
+    public int getRemotePort();
+
+    /* ------------------------------------------------------------ */
+    public boolean isBlocking();
+
+    /* ------------------------------------------------------------ */
+    public boolean blockReadable(long millisecs) throws IOException;
+
+    /* ------------------------------------------------------------ */
+    public boolean blockWritable(long millisecs) throws IOException;
+
+    /* ------------------------------------------------------------ */
+    public boolean isOpen();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The underlying transport object (socket, channel, etc.)
+     */
+    public Object getTransport();
+
+    /* ------------------------------------------------------------ */
+    /** Flush any buffered output.
+     * May fail to write all data if endpoint is non-blocking
+     * @throws EofException If the endpoint is closed or output is shutdown.
+     */
+    public void flush() throws IOException;
+
+    /* ------------------------------------------------------------ */
+    /** Get the max idle time in ms.
+     * <p>The max idle time is the time the endpoint can be idle before
+     * extraordinary handling takes place.  This loosely corresponds to
+     * the {@link java.net.Socket#getSoTimeout()} for blocking connections,
+     * but {@link AsyncEndPoint} implementations must use other mechanisms
+     * to implement the max idle time.
+     * @return the max idle time in ms or if ms <= 0 implies an infinite timeout
+     */
+    public int getMaxIdleTime();
+
+    /* ------------------------------------------------------------ */
+    /** Set the max idle time.
+     * @param timeMs the max idle time in MS. Timeout <= 0 implies an infinite timeout
+     * @throws IOException if the timeout cannot be set.
+     */
+    public void setMaxIdleTime(int timeMs) throws IOException;
+
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/EofException.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,46 @@
+//
+//  ========================================================================
+//  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.io;
+
+import java.io.EOFException;
+
+
+/* ------------------------------------------------------------ */
+/** A Jetty specialization of EOFException.
+ * <p> This is thrown by Jetty to distinguish between EOF received from 
+ * the connection, vs and EOF thrown by some application talking to some other file/socket etc.
+ * The only difference in handling is that Jetty EOFs are logged less verbosely.
+ */
+public class EofException extends EOFException
+{
+    public EofException()
+    {
+    }
+    
+    public EofException(String reason)
+    {
+        super(reason);
+    }
+    
+    public EofException(Throwable th)
+    {
+        if (th!=null)
+            initCause(th);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/NetworkTrafficListener.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,99 @@
+//
+//  ========================================================================
+//  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.io;
+
+import java.net.Socket;
+
+/**
+ * <p>A listener for raw network traffic within Jetty.</p>
+ * <p>{@link NetworkTrafficListener}s can be installed in a
+ * <code>org.eclipse.jetty.server.nio.NetworkTrafficSelectChannelConnector</code>,
+ * and are notified of the following network traffic events:</p>
+ * <ul>
+ * <li>Connection opened, when the server has accepted the connection from a remote client</li>
+ * <li>Incoming bytes, when the server receives bytes sent from a remote client</li>
+ * <li>Outgoing bytes, when the server sends bytes to a remote client</li>
+ * <li>Connection closed, when the server has closed the connection to a remote client</li>
+ * </ul>
+ * <p>{@link NetworkTrafficListener}s can be used to log the network traffic viewed by
+ * a Jetty server (for example logging to filesystem) for activities such as debugging
+ * or request/response cycles or for replaying request/response cycles to other servers.</p>
+ */
+public interface NetworkTrafficListener
+{
+    /**
+     * <p>Callback method invoked when a connection from a remote client has been accepted.</p>
+     * <p>The {@code socket} parameter can be used to extract socket address information of
+     * the remote client.</p>
+     *
+     * @param socket the socket associated with the remote client
+     */
+    public void opened(Socket socket);
+
+    /**
+     * <p>Callback method invoked when bytes sent by a remote client arrived on the server.</p>
+     *
+     * @param socket the socket associated with the remote client
+     * @param bytes  the read-only buffer containing the incoming bytes
+     */
+    public void incoming(Socket socket, Buffer bytes);
+
+    /**
+     * <p>Callback method invoked when bytes are sent to a remote client from the server.</p>
+     * <p>This method is invoked after the bytes have been actually written to the remote client.</p>
+     *
+     * @param socket the socket associated with the remote client
+     * @param bytes  the read-only buffer containing the outgoing bytes
+     */
+    public void outgoing(Socket socket, Buffer bytes);
+
+    /**
+     * <p>Callback method invoked when a connection to a remote client has been closed.</p>
+     * <p>The {@code socket} parameter is already closed when this method is called, so it
+     * cannot be queried for socket address information of the remote client.<br />
+     * However, the {@code socket} parameter is the same object passed to {@link #opened(Socket)},
+     * so it is possible to map socket information in {@link #opened(Socket)} and retrieve it
+     * in this method.
+     *
+     * @param socket the (closed) socket associated with the remote client
+     */
+    public void closed(Socket socket);
+
+    /**
+     * <p>A commodity class that implements {@link NetworkTrafficListener} with empty methods.</p>
+     */
+    public static class Empty implements NetworkTrafficListener
+    {
+        public void opened(Socket socket)
+        {
+        }
+
+        public void incoming(Socket socket, Buffer bytes)
+        {
+        }
+
+        public void outgoing(Socket socket, Buffer bytes)
+        {
+        }
+
+        public void closed(Socket socket)
+        {
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/PooledBuffers.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,122 @@
+//
+//  ========================================================================
+//  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.io;
+
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class PooledBuffers extends AbstractBuffers
+{
+    private final Queue<Buffer> _headers;
+    private final Queue<Buffer> _buffers;
+    private final Queue<Buffer> _others;
+    private final AtomicInteger _size = new AtomicInteger();
+    private final int _maxSize;
+    private final boolean _otherHeaders;
+    private final boolean _otherBuffers;
+
+    /* ------------------------------------------------------------ */
+    public PooledBuffers(Buffers.Type headerType, int headerSize, Buffers.Type bufferType, int bufferSize, Buffers.Type otherType,int maxSize)
+    {
+        super(headerType,headerSize,bufferType,bufferSize,otherType);
+        _headers=new ConcurrentLinkedQueue<Buffer>();
+        _buffers=new ConcurrentLinkedQueue<Buffer>();
+        _others=new ConcurrentLinkedQueue<Buffer>();
+        _otherHeaders=headerType==otherType;
+        _otherBuffers=bufferType==otherType;
+        _maxSize=maxSize;
+    }
+
+    /* ------------------------------------------------------------ */
+    public Buffer getHeader()
+    {
+        Buffer buffer = _headers.poll();
+        if (buffer==null)
+            buffer=newHeader();
+        else
+            _size.decrementAndGet();
+        return buffer;
+    }
+
+    /* ------------------------------------------------------------ */
+    public Buffer getBuffer()
+    {
+        Buffer buffer = _buffers.poll();
+        if (buffer==null)
+            buffer=newBuffer();
+        else
+            _size.decrementAndGet();
+        return buffer;
+    }
+
+    /* ------------------------------------------------------------ */
+    public Buffer getBuffer(int size)
+    {
+        if (_otherHeaders && size==getHeaderSize())
+            return getHeader();
+        if (_otherBuffers && size==getBufferSize())
+            return getBuffer();
+
+        // Look for an other buffer
+        Buffer buffer = _others.poll();
+
+        // consume all other buffers until one of the right size is found
+        while (buffer!=null && buffer.capacity()!=size)
+        {
+            _size.decrementAndGet();
+            buffer = _others.poll();
+        }
+
+        if (buffer==null)
+            buffer=newBuffer(size);
+        else
+            _size.decrementAndGet();
+        return buffer;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void returnBuffer(Buffer buffer)
+    {
+        buffer.clear();
+        if (buffer.isVolatile() || buffer.isImmutable())
+            return;
+
+        if (_size.incrementAndGet() > _maxSize)
+            _size.decrementAndGet();
+        else
+        {
+            if (isHeader(buffer))
+                _headers.add(buffer);
+            else if (isBuffer(buffer))
+                _buffers.add(buffer);
+            else
+                _others.add(buffer);
+        }
+    }
+
+    public String toString()
+    {
+        return String.format("%s [%d/%d@%d,%d/%d@%d,%d/%d@-]",
+                getClass().getSimpleName(),
+                _headers.size(),_maxSize,_headerSize,
+                _buffers.size(),_maxSize,_bufferSize,
+                _others.size(),_maxSize);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/RuntimeIOException.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,48 @@
+//
+//  ========================================================================
+//  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.io;
+
+/* ------------------------------------------------------------ */
+/**
+ * Subclass of {@link java.lang.RuntimeException} used to signal that there
+ * was an {@link java.io.IOException} thrown by underlying {@link java.io.Writer}
+ */
+public class RuntimeIOException extends RuntimeException
+{
+    public RuntimeIOException()
+    {
+        super();
+    }
+
+    public RuntimeIOException(String message)
+    {
+        super(message);
+    }
+
+    public RuntimeIOException(Throwable cause)
+    {
+        super(cause);
+    }
+
+    public RuntimeIOException(String message, Throwable cause)
+    {
+        super(message,cause);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/SimpleBuffers.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,117 @@
+//
+//  ========================================================================
+//  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.io;
+
+/* ------------------------------------------------------------ */
+/** SimpleBuffers.
+ * Simple implementation of Buffers holder.
+ * 
+ *
+ */
+public class SimpleBuffers implements Buffers
+{   
+    final Buffer _header;
+    final Buffer _buffer;
+    boolean _headerOut;
+    boolean _bufferOut;
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * 
+     */
+    public SimpleBuffers(Buffer header, Buffer buffer)
+    {
+        _header=header;
+        _buffer=buffer;
+    }
+
+    /* ------------------------------------------------------------ */
+    public Buffer getBuffer()
+    {
+        synchronized(this)
+        {
+            if (_buffer!=null && !_bufferOut)
+            {
+                _bufferOut=true;
+                return _buffer;
+            }
+            
+            if (_buffer!=null && _header!=null && _header.capacity()==_buffer.capacity() && !_headerOut)
+            {
+                _headerOut=true;
+                return _header;
+            }
+            
+            if (_buffer!=null)
+                return new ByteArrayBuffer(_buffer.capacity());
+            return new ByteArrayBuffer(4096);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public Buffer getHeader()
+    {
+        synchronized(this)
+        {
+            if (_header!=null && !_headerOut)
+            {
+                _headerOut=true;
+                return _header;
+            }
+            
+            if (_buffer!=null && _header!=null && _header.capacity()==_buffer.capacity() && !_bufferOut)
+            {
+                _bufferOut=true;
+                return _buffer;
+            }
+            
+            if (_header!=null)
+                return new ByteArrayBuffer(_header.capacity());
+            return new ByteArrayBuffer(4096);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public Buffer getBuffer(int size)
+    {
+        synchronized(this)
+        {
+            if (_header!=null && _header.capacity()==size)
+                return getHeader();
+            if (_buffer!=null && _buffer.capacity()==size)
+                return getBuffer();
+            return null;            
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public void returnBuffer(Buffer buffer)
+    {
+        synchronized(this)
+        {
+            buffer.clear();
+            if (buffer==_header)
+                _headerOut=false;
+            if (buffer==_buffer)
+                _bufferOut=false;
+        }
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/ThreadLocalBuffers.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,135 @@
+//
+//  ========================================================================
+//  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.io;
+
+
+
+/* ------------------------------------------------------------ */
+/** Abstract Buffer pool.
+ * simple unbounded pool of buffers for header, request and response sizes.
+ *
+ */
+public class ThreadLocalBuffers extends AbstractBuffers 
+{
+    /* ------------------------------------------------------------ */
+    private final ThreadLocal<ThreadBuffers> _buffers=new ThreadLocal<ThreadBuffers>()
+    {
+        @Override
+        protected ThreadBuffers initialValue()
+        {
+            return new ThreadBuffers();
+        }
+    };
+
+    /* ------------------------------------------------------------ */
+    public ThreadLocalBuffers(Buffers.Type headerType, int headerSize, Buffers.Type bufferType, int bufferSize, Buffers.Type otherType)
+    {
+        super(headerType,headerSize,bufferType,bufferSize,otherType);
+    }
+
+    /* ------------------------------------------------------------ */
+    public Buffer getBuffer()
+    {
+        ThreadBuffers buffers = _buffers.get();
+        if (buffers._buffer!=null)
+        {
+            Buffer b=buffers._buffer;
+            buffers._buffer=null;
+            return b;
+        }
+
+        if (buffers._other!=null && isBuffer(buffers._other))
+        {
+            Buffer b=buffers._other;
+            buffers._other=null;
+            return b;
+        }
+
+        return newBuffer();
+    }
+
+    /* ------------------------------------------------------------ */
+    public Buffer getHeader()
+    {
+        ThreadBuffers buffers = _buffers.get();
+        if (buffers._header!=null)
+        {
+            Buffer b=buffers._header;
+            buffers._header=null;
+            return b;
+        }
+
+        if (buffers._other!=null && isHeader(buffers._other))
+        {
+            Buffer b=buffers._other;
+            buffers._other=null;
+            return b;
+        }
+
+        return newHeader();
+    }
+
+    /* ------------------------------------------------------------ */
+    public Buffer getBuffer(int size)
+    {
+        ThreadBuffers buffers = _buffers.get();
+        if (buffers._other!=null && buffers._other.capacity()==size)
+        {
+            Buffer b=buffers._other;
+            buffers._other=null;
+            return b;
+        }
+
+        return newBuffer(size);
+    }
+
+    /* ------------------------------------------------------------ */
+    public void returnBuffer(Buffer buffer)
+    {
+        buffer.clear();
+        if (buffer.isVolatile() || buffer.isImmutable())
+            return;
+        
+        ThreadBuffers buffers = _buffers.get();
+        
+        if (buffers._header==null && isHeader(buffer))
+            buffers._header=buffer;
+        else if (buffers._buffer==null && isBuffer(buffer))
+            buffers._buffer=buffer;
+        else
+            buffers._other=buffer;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public String toString()
+    {
+        return "{{"+getHeaderSize()+","+getBufferSize()+"}}";
+    }
+    
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    protected static class ThreadBuffers
+    {
+        Buffer _buffer;
+        Buffer _header;
+        Buffer _other;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/UncheckedIOException.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,48 @@
+//
+//  ========================================================================
+//  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.io;
+
+/* ------------------------------------------------------------ */
+/**
+ * Subclass of {@link java.lang.RuntimeException} used to signal that there
+ * was an {@link java.io.IOException} thrown by underlying {@link UncheckedPrintWriter}
+ */
+public class UncheckedIOException extends RuntimeException
+{
+    public UncheckedIOException()
+    {
+        super();
+    }
+
+    public UncheckedIOException(String message)
+    {
+        super(message);
+    }
+
+    public UncheckedIOException(Throwable cause)
+    {
+        super(cause);
+    }
+
+    public UncheckedIOException(String message, Throwable cause)
+    {
+        super(message,cause);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/UncheckedPrintWriter.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,682 @@
+//
+//  ========================================================================
+//  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.io;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Writer;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/* ------------------------------------------------------------ */
+/**
+ * A wrapper for the {@link java.io.PrintWriter} that re-throws the instances of
+ * {@link java.io.IOException} thrown by the underlying implementation of
+ * {@link java.io.Writer} as {@link RuntimeIOException} instances.
+ */
+public class UncheckedPrintWriter extends PrintWriter
+{
+    private static final Logger LOG = Log.getLogger(UncheckedPrintWriter.class);
+
+    private boolean _autoFlush = false;
+    private IOException _ioException;
+    private boolean _isClosed = false;
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Line separator string. This is the value of the line.separator property
+     * at the moment that the stream was created.
+     */
+    private String _lineSeparator;
+
+    public UncheckedPrintWriter(Writer out)
+    {
+        this(out,false);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Create a new PrintWriter.
+     * 
+     * @param out
+     *            A character-output stream
+     * @param autoFlush
+     *            A boolean; if true, the println() methods will flush the
+     *            output buffer
+     */
+    public UncheckedPrintWriter(Writer out, boolean autoFlush)
+    {
+        super(out,autoFlush);
+        this._autoFlush = autoFlush;
+        this._lineSeparator = System.getProperty("line.separator");
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Create a new PrintWriter, without automatic line flushing, from an
+     * existing OutputStream. This convenience constructor creates the necessary
+     * intermediate OutputStreamWriter, which will convert characters into bytes
+     * using the default character encoding.
+     * 
+     * @param out
+     *            An output stream
+     * 
+     * @see java.io.OutputStreamWriter#OutputStreamWriter(java.io.OutputStream)
+     */
+    public UncheckedPrintWriter(OutputStream out)
+    {
+        this(out,false);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Create a new PrintWriter from an existing OutputStream. This convenience
+     * constructor creates the necessary intermediate OutputStreamWriter, which
+     * will convert characters into bytes using the default character encoding.
+     * 
+     * @param out
+     *            An output stream
+     * @param autoFlush
+     *            A boolean; if true, the println() methods will flush the
+     *            output buffer
+     * 
+     * @see java.io.OutputStreamWriter#OutputStreamWriter(java.io.OutputStream)
+     */
+    public UncheckedPrintWriter(OutputStream out, boolean autoFlush)
+    {
+        this(new BufferedWriter(new OutputStreamWriter(out)),autoFlush);
+    }
+    
+    
+    /* ------------------------------------------------------------ */
+    public boolean checkError()
+    {
+        return _ioException!=null || super.checkError();
+    }
+    
+    /* ------------------------------------------------------------ */
+    private void setError(Throwable th)
+    {
+      
+        super.setError();
+
+        if (th instanceof IOException)
+            _ioException=(IOException)th;
+        else
+        {
+            _ioException=new IOException(String.valueOf(th));
+            _ioException.initCause(th);
+        }
+
+        LOG.debug(th);
+    }
+
+
+    @Override
+    protected void setError()
+    {
+        setError(new IOException());
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Check to make sure that the stream has not been closed */
+    private void isOpen() throws IOException
+    {       
+        if (_ioException!=null)
+            throw new RuntimeIOException(_ioException); 
+        
+        if (_isClosed)
+            throw new IOException("Stream closed");
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Flush the stream.
+     */
+    @Override
+    public void flush()
+    {
+        try
+        {
+            synchronized (lock)
+            {
+                isOpen();
+                out.flush();
+            }
+        }
+        catch (IOException ex)
+        {
+            setError(ex);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Close the stream.
+     */
+    @Override
+    public void close()
+    {
+        try
+        {
+            synchronized (lock)
+            {
+                out.close();
+                _isClosed = true;
+            }
+        }
+        catch (IOException ex)
+        {
+            setError(ex);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Write a single character.
+     * 
+     * @param c
+     *            int specifying a character to be written.
+     */
+    @Override
+    public void write(int c)
+    {
+        try
+        {
+            synchronized (lock)
+            {
+                isOpen();
+                out.write(c);
+            }
+        }
+        catch (InterruptedIOException x)
+        {
+            Thread.currentThread().interrupt();
+        }
+        catch (IOException ex)
+        {
+            setError(ex);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Write a portion of an array of characters.
+     * 
+     * @param buf
+     *            Array of characters
+     * @param off
+     *            Offset from which to start writing characters
+     * @param len
+     *            Number of characters to write
+     */
+    @Override
+    public void write(char buf[], int off, int len)
+    {
+        try
+        {
+            synchronized (lock)
+            {
+                isOpen();
+                out.write(buf,off,len);
+            }
+        }
+        catch (InterruptedIOException x)
+        {
+            Thread.currentThread().interrupt();
+        }
+        catch (IOException ex)
+        {
+            setError(ex);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Write an array of characters. This method cannot be inherited from the
+     * Writer class because it must suppress I/O exceptions.
+     * 
+     * @param buf
+     *            Array of characters to be written
+     */
+    @Override
+    public void write(char buf[])
+    { 
+        this.write(buf,0,buf.length);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Write a portion of a string.
+     * 
+     * @param s
+     *            A String
+     * @param off
+     *            Offset from which to start writing characters
+     * @param len
+     *            Number of characters to write
+     */
+    @Override
+    public void write(String s, int off, int len)
+    {
+        try
+        {
+            synchronized (lock)
+            {
+                isOpen();
+                out.write(s,off,len);
+            }
+        }
+        catch (InterruptedIOException x)
+        {
+            Thread.currentThread().interrupt();
+        }
+        catch (IOException ex)
+        {
+            setError(ex);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Write a string. This method cannot be inherited from the Writer class
+     * because it must suppress I/O exceptions.
+     * 
+     * @param s
+     *            String to be written
+     */
+    @Override
+    public void write(String s)
+    {
+        this.write(s,0,s.length());
+    }
+
+    private void newLine()
+    {
+        try
+        {
+            synchronized (lock)
+            {
+                isOpen();
+                out.write(_lineSeparator);
+                if (_autoFlush)
+                    out.flush();
+            }
+        }
+        catch (InterruptedIOException x)
+        {
+            Thread.currentThread().interrupt();
+        }
+        catch (IOException ex)
+        {
+            setError(ex);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Print a boolean value. The string produced by <code>{@link
+     * java.lang.String#valueOf(boolean)}</code> is translated into bytes
+     * according to the platform's default character encoding, and these bytes
+     * are written in exactly the manner of the <code>{@link
+     * #write(int)}</code> method.
+     * 
+     * @param b
+     *            The <code>boolean</code> to be printed
+     */
+    @Override
+    public void print(boolean b)
+    {
+        this.write(b?"true":"false");
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Print a character. The character is translated into one or more bytes
+     * according to the platform's default character encoding, and these bytes
+     * are written in exactly the manner of the <code>{@link
+     * #write(int)}</code> method.
+     * 
+     * @param c
+     *            The <code>char</code> to be printed
+     */
+    @Override
+    public void print(char c)
+    {
+        this.write(c);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Print an integer. The string produced by <code>{@link
+     * java.lang.String#valueOf(int)}</code> is translated into bytes according
+     * to the platform's default character encoding, and these bytes are written
+     * in exactly the manner of the <code>{@link #write(int)}</code> method.
+     * 
+     * @param i
+     *            The <code>int</code> to be printed
+     * @see java.lang.Integer#toString(int)
+     */
+    @Override
+    public void print(int i)
+    {
+        this.write(String.valueOf(i));
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Print a long integer. The string produced by <code>{@link
+     * java.lang.String#valueOf(long)}</code> is translated into bytes according
+     * to the platform's default character encoding, and these bytes are written
+     * in exactly the manner of the <code>{@link #write(int)}</code> method.
+     * 
+     * @param l
+     *            The <code>long</code> to be printed
+     * @see java.lang.Long#toString(long)
+     */
+    @Override
+    public void print(long l)
+    {
+        this.write(String.valueOf(l));
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Print a floating-point number. The string produced by <code>{@link
+     * java.lang.String#valueOf(float)}</code> is translated into bytes
+     * according to the platform's default character encoding, and these bytes
+     * are written in exactly the manner of the <code>{@link #write(int)}</code>
+     * method.
+     * 
+     * @param f
+     *            The <code>float</code> to be printed
+     * @see java.lang.Float#toString(float)
+     */
+    @Override
+    public void print(float f)
+    {
+        this.write(String.valueOf(f));
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Print a double-precision floating-point number. The string produced by
+     * <code>{@link java.lang.String#valueOf(double)}</code> is translated into
+     * bytes according to the platform's default character encoding, and these
+     * bytes are written in exactly the manner of the <code>{@link
+     * #write(int)}</code> method.
+     * 
+     * @param d
+     *            The <code>double</code> to be printed
+     * @see java.lang.Double#toString(double)
+     */
+    @Override
+    public void print(double d)
+    {
+        this.write(String.valueOf(d));
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Print an array of characters. The characters are converted into bytes
+     * according to the platform's default character encoding, and these bytes
+     * are written in exactly the manner of the <code>{@link #write(int)}</code>
+     * method.
+     * 
+     * @param s
+     *            The array of chars to be printed
+     * 
+     * @throws NullPointerException
+     *             If <code>s</code> is <code>null</code>
+     */
+    @Override
+    public void print(char s[])
+    {
+        this.write(s);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Print a string. If the argument is <code>null</code> then the string
+     * <code>"null"</code> is printed. Otherwise, the string's characters are
+     * converted into bytes according to the platform's default character
+     * encoding, and these bytes are written in exactly the manner of the
+     * <code>{@link #write(int)}</code> method.
+     * 
+     * @param s
+     *            The <code>String</code> to be printed
+     */
+    @Override
+    public void print(String s)
+    {
+        if (s == null)
+        {
+            s = "null";
+        }
+        this.write(s);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Print an object. The string produced by the <code>{@link
+     * java.lang.String#valueOf(Object)}</code> method is translated into bytes
+     * according to the platform's default character encoding, and these bytes
+     * are written in exactly the manner of the <code>{@link #write(int)}</code>
+     * method.
+     * 
+     * @param obj
+     *            The <code>Object</code> to be printed
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public void print(Object obj)
+    {
+        this.write(String.valueOf(obj));
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Terminate the current line by writing the line separator string. The line
+     * separator string is defined by the system property
+     * <code>line.separator</code>, and is not necessarily a single newline
+     * character (<code>'\n'</code>).
+     */
+    @Override
+    public void println()
+    {
+        this.newLine();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Print a boolean value and then terminate the line. This method behaves as
+     * though it invokes <code>{@link #print(boolean)}</code> and then
+     * <code>{@link #println()}</code>.
+     * 
+     * @param x
+     *            the <code>boolean</code> value to be printed
+     */
+    @Override
+    public void println(boolean x)
+    {
+        synchronized (lock)
+        {
+            this.print(x);
+            this.println();
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Print a character and then terminate the line. This method behaves as
+     * though it invokes <code>{@link #print(char)}</code> and then <code>{@link
+     * #println()}</code>.
+     * 
+     * @param x
+     *            the <code>char</code> value to be printed
+     */
+    @Override
+    public void println(char x)
+    {
+        synchronized (lock)
+        {
+            this.print(x);
+            this.println();
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Print an integer and then terminate the line. This method behaves as
+     * though it invokes <code>{@link #print(int)}</code> and then <code>{@link
+     * #println()}</code>.
+     * 
+     * @param x
+     *            the <code>int</code> value to be printed
+     */
+    @Override
+    public void println(int x)
+    {
+        synchronized (lock)
+        {
+            this.print(x);
+            this.println();
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Print a long integer and then terminate the line. This method behaves as
+     * though it invokes <code>{@link #print(long)}</code> and then
+     * <code>{@link #println()}</code>.
+     * 
+     * @param x
+     *            the <code>long</code> value to be printed
+     */
+    @Override
+    public void println(long x)
+    {
+        synchronized (lock)
+        {
+            this.print(x);
+            this.println();
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Print a floating-point number and then terminate the line. This method
+     * behaves as though it invokes <code>{@link #print(float)}</code> and then
+     * <code>{@link #println()}</code>.
+     * 
+     * @param x
+     *            the <code>float</code> value to be printed
+     */
+    @Override
+    public void println(float x)
+    {
+        synchronized (lock)
+        {
+            this.print(x);
+            this.println();
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Print a double-precision floating-point number and then terminate the
+     * line. This method behaves as though it invokes <code>{@link
+     * #print(double)}</code> and then <code>{@link #println()}</code>.
+     * 
+     * @param x
+     *            the <code>double</code> value to be printed
+     */
+    /* ------------------------------------------------------------ */
+    @Override
+    public void println(double x)
+    {
+        synchronized (lock)
+        {
+            this.print(x);
+            this.println();
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Print an array of characters and then terminate the line. This method
+     * behaves as though it invokes <code>{@link #print(char[])}</code> and then
+     * <code>{@link #println()}</code>.
+     * 
+     * @param x
+     *            the array of <code>char</code> values to be printed
+     */
+    @Override
+    public void println(char x[])
+    {
+        synchronized (lock)
+        {
+            this.print(x);
+            this.println();
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Print a String and then terminate the line. This method behaves as though
+     * it invokes <code>{@link #print(String)}</code> and then
+     * <code>{@link #println()}</code>.
+     * 
+     * @param x
+     *            the <code>String</code> value to be printed
+     */
+    @Override
+    public void println(String x)
+    {
+        synchronized (lock)
+        {
+            this.print(x);
+            this.println();
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Print an Object and then terminate the line. This method behaves as
+     * though it invokes <code>{@link #print(Object)}</code> and then
+     * <code>{@link #println()}</code>.
+     * 
+     * @param x
+     *            the <code>Object</code> value to be printed
+     */
+    @Override
+    public void println(Object x)
+    {
+        synchronized (lock)
+        {
+            this.print(x);
+            this.println();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/View.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,251 @@
+//
+//  ========================================================================
+//  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.io;
+
+/**
+ * A View on another buffer.  Allows operations that do not change the _content or
+ * indexes of the backing buffer.
+ * 
+ * 
+ * 
+ */
+public class View extends AbstractBuffer
+{
+    Buffer _buffer;
+
+    /**
+     * @param buffer The <code>Buffer</code> on which we are presenting a <code>View</code>.
+     * @param mark The initial value of the {@link Buffer#markIndex mark index}
+     * @param get The initial value of the {@link Buffer#getIndex get index}
+     * @param put The initial value of the {@link Buffer#putIndex put index}
+     * @param access The access level - one of the constants from {@link Buffer}.
+     */
+    public View(Buffer buffer, int mark, int get, int put,int access)
+    {
+        super(READWRITE,!buffer.isImmutable());
+        _buffer=buffer.buffer();
+        setPutIndex(put);
+        setGetIndex(get);
+        setMarkIndex(mark);
+        _access=access;
+    }
+    
+    public View(Buffer buffer)
+    {
+        super(READWRITE,!buffer.isImmutable());
+        _buffer=buffer.buffer();
+        setPutIndex(buffer.putIndex());
+        setGetIndex(buffer.getIndex());
+        setMarkIndex(buffer.markIndex());
+        _access=buffer.isReadOnly()?READONLY:READWRITE;
+    }
+
+    public View()
+    {
+        super(READWRITE,true);
+    }
+    
+    /**
+     * Update view to buffer
+     */
+    public void update(Buffer buffer)
+    {
+        _access=READWRITE;
+        _buffer=buffer.buffer();
+        setGetIndex(0);
+        setPutIndex(buffer.putIndex());
+        setGetIndex(buffer.getIndex());
+        setMarkIndex(buffer.markIndex());
+        _access=buffer.isReadOnly()?READONLY:READWRITE;
+    }
+
+    public void update(int get, int put)
+    {
+        int a=_access;
+        _access=READWRITE;
+        setGetIndex(0);
+        setPutIndex(put);
+        setGetIndex(get);
+        setMarkIndex(-1);
+        _access=a;
+    }
+
+    /**
+     * @return The {@link Buffer#array()} from the underlying buffer.
+     */
+    public byte[] array()
+    {
+        return _buffer.array();
+    }
+
+    /**
+     * @return The {@link Buffer#buffer()} from the underlying buffer.
+     */
+    @Override
+    public Buffer buffer()
+    {
+        return _buffer.buffer();
+    }
+
+    /**
+     * @return The {@link Buffer#capacity} of the underlying buffer.
+     */
+    public int capacity()
+    {
+        return _buffer.capacity();
+    }
+
+    /**
+     *  
+     */
+    @Override
+    public void clear()
+    {
+        setMarkIndex(-1);
+        setGetIndex(0);
+        setPutIndex(_buffer.getIndex());
+        setGetIndex(_buffer.getIndex());
+    }
+
+    /**
+     *  
+     */
+    @Override
+    public void compact()
+    {
+        // TODO
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj)
+    {
+        return  this==obj ||((obj instanceof Buffer)&& obj.equals(this)) || super.equals(obj);
+    }
+
+    /**
+     * @return Whether the underlying buffer is {@link Buffer#isReadOnly read only}
+     */
+    @Override
+    public boolean isReadOnly()
+    {
+        return _buffer.isReadOnly();
+    }
+
+    /**
+     * @return Whether the underlying buffer is {@link Buffer#isVolatile volatile}
+     */
+    @Override
+    public boolean isVolatile()
+    {
+        return true;
+    }
+
+    /**
+     * @return The result of calling {@link Buffer#peek(int)} on the underlying buffer
+     */
+    public byte peek(int index)
+    {
+        return _buffer.peek(index);
+    }
+
+    /**
+     * @return The result of calling {@link Buffer#peek(int, byte[], int, int)} on the underlying buffer
+     */
+    public int peek(int index, byte[] b, int offset, int length)
+    {
+        return _buffer.peek(index,b,offset,length);
+    }
+
+    /**
+     * @return The result of calling {@link Buffer#peek(int, int)} on the underlying buffer
+     */
+    @Override
+    public Buffer peek(int index, int length)
+    {
+        return _buffer.peek(index, length);
+    }
+    
+    /**
+     * @param index
+     * @param src
+     */
+    @Override
+    public int poke(int index, Buffer src)
+    {
+        return _buffer.poke(index,src); 
+    }
+
+    /**
+     * @param index
+     * @param b
+     */
+    public void poke(int index, byte b)
+    {
+        _buffer.poke(index,b);
+    }
+
+    /**
+     * @param index
+     * @param b
+     * @param offset
+     * @param length
+     */
+    @Override
+    public int poke(int index, byte[] b, int offset, int length)
+    {
+        return _buffer.poke(index,b,offset,length);
+    }
+    
+    @Override
+    public String toString()
+    {
+        if (_buffer==null)
+            return "INVALID";
+        return super.toString();
+    }
+    
+    public static class CaseInsensitive extends View implements Buffer.CaseInsensitve
+    {
+        public CaseInsensitive()
+        {
+            super();
+        }
+
+        public CaseInsensitive(Buffer buffer, int mark, int get, int put, int access)
+        {
+            super(buffer,mark,get,put,access);
+        }
+
+        public CaseInsensitive(Buffer buffer)
+        {
+            super(buffer);
+        }
+        
+        @Override
+        public boolean equals(Object obj)
+        {
+            return  this==obj ||((obj instanceof Buffer)&&((Buffer)obj).equalsIgnoreCase(this)) || super.equals(obj);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/WriterOutputStream.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,100 @@
+//
+//  ========================================================================
+//  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.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+
+
+/* ------------------------------------------------------------ */
+/** Wrap a Writer as an OutputStream.
+ * When all you have is a Writer and only an OutputStream will do.
+ * Try not to use this as it indicates that your design is a dogs
+ * breakfast (JSP made me write it).
+ * 
+ */
+public class WriterOutputStream extends OutputStream
+{
+    protected final Writer _writer;
+    protected final String _encoding;
+    private final byte[] _buf=new byte[1];
+    
+    /* ------------------------------------------------------------ */
+    public WriterOutputStream(Writer writer, String encoding)
+    {
+        _writer=writer;
+        _encoding=encoding;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public WriterOutputStream(Writer writer)
+    {
+        _writer=writer;
+        _encoding=null;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void close()
+        throws IOException
+    {
+        _writer.close();
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public void flush()
+        throws IOException
+    {
+        _writer.flush();
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public void write(byte[] b) 
+        throws IOException
+    {
+        if (_encoding==null)
+            _writer.write(new String(b));
+        else
+            _writer.write(new String(b,_encoding));
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public void write(byte[] b, int off, int len)
+        throws IOException
+    {
+        if (_encoding==null)
+            _writer.write(new String(b,off,len));
+        else
+            _writer.write(new String(b,off,len,_encoding));
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public synchronized void write(int b)
+        throws IOException
+    {
+        _buf[0]=(byte)b;
+        write(_buf);
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/bio/SocketEndPoint.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,286 @@
+//
+//  ========================================================================
+//  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.io.bio;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+
+import javax.net.ssl.SSLSocket;
+
+import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+public class SocketEndPoint extends StreamEndPoint
+{
+    private static final Logger LOG = Log.getLogger(SocketEndPoint.class);
+
+    final Socket _socket;
+    final InetSocketAddress _local;
+    final InetSocketAddress _remote;
+
+    /* ------------------------------------------------------------ */
+    /**
+     *
+     */
+    public SocketEndPoint(Socket socket)
+    	throws IOException
+    {
+        super(socket.getInputStream(),socket.getOutputStream());
+        _socket=socket;
+        _local=(InetSocketAddress)_socket.getLocalSocketAddress();
+        _remote=(InetSocketAddress)_socket.getRemoteSocketAddress();
+        super.setMaxIdleTime(_socket.getSoTimeout());
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     *
+     */
+    protected SocketEndPoint(Socket socket, int maxIdleTime)
+        throws IOException
+    {
+        super(socket.getInputStream(),socket.getOutputStream());
+        _socket=socket;
+        _local=(InetSocketAddress)_socket.getLocalSocketAddress();
+        _remote=(InetSocketAddress)_socket.getRemoteSocketAddress();
+        _socket.setSoTimeout(maxIdleTime>0?maxIdleTime:0);
+        super.setMaxIdleTime(maxIdleTime);
+    }
+
+    /* ------------------------------------------------------------ */
+    /* (non-Javadoc)
+     * @see org.eclipse.io.BufferIO#isClosed()
+     */
+    @Override
+    public boolean isOpen()
+    {
+        return super.isOpen() && _socket!=null && !_socket.isClosed();
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public boolean isInputShutdown()
+    {
+        if (_socket instanceof SSLSocket)
+            return super.isInputShutdown();
+        return _socket.isClosed() || _socket.isInputShutdown();
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public boolean isOutputShutdown()
+    {
+        if (_socket instanceof SSLSocket)
+            return super.isOutputShutdown();
+
+        return _socket.isClosed() || _socket.isOutputShutdown();
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /*
+     */
+    protected final void shutdownSocketOutput() throws IOException
+    {
+        if (!_socket.isClosed())
+        {
+            if (!_socket.isOutputShutdown())
+                _socket.shutdownOutput();
+            if (_socket.isInputShutdown())
+                _socket.close();
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.jetty.io.bio.StreamEndPoint#shutdownOutput()
+     */
+    @Override
+    public void shutdownOutput() throws IOException
+    {
+        if (_socket instanceof SSLSocket)
+            super.shutdownOutput();
+        else
+            shutdownSocketOutput();
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /*
+     */
+    public void shutdownSocketInput() throws IOException
+    {
+        if (!_socket.isClosed())
+        {
+            if (!_socket.isInputShutdown())
+                _socket.shutdownInput();
+            if (_socket.isOutputShutdown())
+                _socket.close();
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.jetty.io.bio.StreamEndPoint#shutdownOutput()
+     */
+    @Override
+    public void shutdownInput() throws IOException
+    {
+        if (_socket instanceof SSLSocket)
+            super.shutdownInput();
+        else
+            shutdownSocketInput();
+    }
+
+    /* ------------------------------------------------------------ */
+    /* (non-Javadoc)
+     * @see org.eclipse.io.BufferIO#close()
+     */
+    @Override
+    public void close() throws IOException
+    {
+        _socket.close();
+        _in=null;
+        _out=null;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.io.EndPoint#getLocalAddr()
+     */
+    @Override
+    public String getLocalAddr()
+    {
+       if (_local==null || _local.getAddress()==null || _local.getAddress().isAnyLocalAddress())
+           return StringUtil.ALL_INTERFACES;
+
+        return _local.getAddress().getHostAddress();
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.io.EndPoint#getLocalHost()
+     */
+    @Override
+    public String getLocalHost()
+    {
+       if (_local==null || _local.getAddress()==null || _local.getAddress().isAnyLocalAddress())
+           return StringUtil.ALL_INTERFACES;
+
+        return _local.getAddress().getCanonicalHostName();
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.io.EndPoint#getLocalPort()
+     */
+    @Override
+    public int getLocalPort()
+    {
+        if (_local==null)
+            return -1;
+        return _local.getPort();
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.io.EndPoint#getRemoteAddr()
+     */
+    @Override
+    public String getRemoteAddr()
+    {
+        if (_remote==null)
+            return null;
+        InetAddress addr = _remote.getAddress();
+        return ( addr == null ? null : addr.getHostAddress() );
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.io.EndPoint#getRemoteHost()
+     */
+    @Override
+    public String getRemoteHost()
+    {
+        if (_remote==null)
+            return null;
+        return _remote.getAddress().getCanonicalHostName();
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.io.EndPoint#getRemotePort()
+     */
+    @Override
+    public int getRemotePort()
+    {
+        if (_remote==null)
+            return -1;
+        return _remote.getPort();
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.io.EndPoint#getConnection()
+     */
+    @Override
+    public Object getTransport()
+    {
+        return _socket;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.io.bio.StreamEndPoint#setMaxIdleTime(int)
+     */
+    @Override
+    public void setMaxIdleTime(int timeMs) throws IOException
+    {
+        if (timeMs!=getMaxIdleTime())
+            _socket.setSoTimeout(timeMs>0?timeMs:0);
+        super.setMaxIdleTime(timeMs);
+    }
+
+
+    /* ------------------------------------------------------------ */
+    @Override
+    protected void idleExpired() throws IOException
+    {
+        try
+        {
+            if (!isInputShutdown())
+                shutdownInput();
+        }
+        catch(IOException e)
+        {
+            LOG.ignore(e);
+            _socket.close();
+        }
+    }
+
+    @Override
+    public String toString()
+    {
+        return _local + " <--> " + _remote;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/bio/StreamEndPoint.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,325 @@
+//
+//  ========================================================================
+//  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.io.bio;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.SocketTimeoutException;
+
+import org.eclipse.jetty.io.Buffer;
+import org.eclipse.jetty.io.EndPoint;
+
+public class StreamEndPoint implements EndPoint
+{
+    InputStream _in;
+    OutputStream _out;
+    int _maxIdleTime;
+    boolean _ishut;
+    boolean _oshut;
+
+    /**
+     *
+     */
+    public StreamEndPoint(InputStream in, OutputStream out)
+    {
+        _in=in;
+        _out=out;
+    }
+
+    public boolean isBlocking()
+    {
+        return true;
+    }
+
+    public boolean blockReadable(long millisecs) throws IOException
+    {
+        return true;
+    }
+
+    public boolean blockWritable(long millisecs) throws IOException
+    {
+        return true;
+    }
+
+    /*
+     * @see org.eclipse.io.BufferIO#isOpen()
+     */
+    public boolean isOpen()
+    {
+        return _in!=null;
+    }
+
+    /*
+     * @see org.eclipse.io.BufferIO#isOpen()
+     */
+    public final boolean isClosed()
+    {
+        return !isOpen();
+    }
+
+    public void shutdownOutput() throws IOException
+    {
+        _oshut = true;
+        if (_ishut && _out!=null)
+            _out.close();
+    }
+
+    public boolean isInputShutdown()
+    {
+        return _ishut;
+    }
+
+    public void shutdownInput() throws IOException
+    {
+        _ishut = true;
+        if (_oshut&&_in!=null)
+            _in.close();
+    }
+
+    public boolean isOutputShutdown()
+    {
+        return _oshut;
+    }
+
+    /*
+     * @see org.eclipse.io.BufferIO#close()
+     */
+    public void close() throws IOException
+    {
+        if (_in!=null)
+            _in.close();
+        _in=null;
+        if (_out!=null)
+            _out.close();
+        _out=null;
+    }
+
+    protected void idleExpired() throws IOException
+    {
+        if (_in!=null)
+            _in.close();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.io.BufferIO#fill(org.eclipse.io.Buffer)
+     */
+    public int fill(Buffer buffer) throws IOException
+    {
+        if (_ishut)
+            return -1;
+        if (_in==null)
+            return 0;
+
+        int space=buffer.space();
+        if (space<=0)
+        {
+            if (buffer.hasContent())
+                return 0;
+            throw new IOException("FULL");
+        }
+
+        try
+        {
+            int filled=buffer.readFrom(_in, space);
+            if (filled<0)
+                shutdownInput();
+            return filled;
+        }
+        catch(SocketTimeoutException e)
+        {
+            idleExpired();
+            return -1;
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.io.BufferIO#flush(org.eclipse.io.Buffer)
+     */
+    public int flush(Buffer buffer) throws IOException
+    {
+        if (_oshut)
+            return -1;
+        if (_out==null)
+            return 0;
+        int length=buffer.length();
+        if (length>0)
+            buffer.writeTo(_out);
+        if (!buffer.isImmutable())
+            buffer.clear();
+        return length;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.io.BufferIO#flush(org.eclipse.io.Buffer, org.eclipse.io.Buffer, org.eclipse.io.Buffer)
+     */
+    public int flush(Buffer header, Buffer buffer, Buffer trailer) throws IOException
+    {
+        int len=0;
+
+        if (header!=null)
+        {
+            int tw=header.length();
+            if (tw>0)
+            {
+                int f=flush(header);
+                len=f;
+                if (f<tw)
+                    return len;
+            }
+        }
+
+        if (buffer!=null)
+        {
+            int tw=buffer.length();
+            if (tw>0)
+            {
+                int f=flush(buffer);
+                if (f<0)
+                    return len>0?len:f;
+                len+=f;
+                if (f<tw)
+                    return len;
+            }
+        }
+
+        if (trailer!=null)
+        {
+            int tw=trailer.length();
+            if (tw>0)
+            {
+                int f=flush(trailer);
+                if (f<0)
+                    return len>0?len:f;
+                len+=f;
+            }
+        }
+        return len;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.io.EndPoint#getLocalAddr()
+     */
+    public String getLocalAddr()
+    {
+        return null;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.io.EndPoint#getLocalHost()
+     */
+    public String getLocalHost()
+    {
+        return null;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.io.EndPoint#getLocalPort()
+     */
+    public int getLocalPort()
+    {
+        return 0;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.io.EndPoint#getRemoteAddr()
+     */
+    public String getRemoteAddr()
+    {
+        return null;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.io.EndPoint#getRemoteHost()
+     */
+    public String getRemoteHost()
+    {
+        return null;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.io.EndPoint#getRemotePort()
+     */
+    public int getRemotePort()
+    {
+        return 0;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.io.EndPoint#getConnection()
+     */
+    public Object getTransport()
+    {
+        return null;
+    }
+
+    /* ------------------------------------------------------------ */
+    public InputStream getInputStream()
+    {
+        return _in;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setInputStream(InputStream in)
+    {
+        _in=in;
+    }
+
+    /* ------------------------------------------------------------ */
+    public OutputStream getOutputStream()
+    {
+        return _out;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setOutputStream(OutputStream out)
+    {
+        _out=out;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    public void flush()
+        throws IOException
+    {
+        if (_out != null)
+            _out.flush();
+    }
+
+    /* ------------------------------------------------------------ */
+    public int getMaxIdleTime()
+    {
+        return _maxIdleTime;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setMaxIdleTime(int timeMs) throws IOException
+    {
+        _maxIdleTime=timeMs;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/bio/StringEndPoint.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,94 @@
+//
+//  ========================================================================
+//  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.io.bio;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+
+import org.eclipse.jetty.util.StringUtil;
+
+/**
+ * 
+ *
+ * To change the template for this generated type comment go to
+ * Window - Preferences - Java - Code Generation - Code and Comments
+ */
+public class StringEndPoint extends StreamEndPoint
+{
+    String _encoding=StringUtil.__UTF8;
+    ByteArrayInputStream _bin = new ByteArrayInputStream(new byte[0]);
+    ByteArrayOutputStream _bout = new ByteArrayOutputStream();
+    
+    public StringEndPoint()
+    {
+        super(null,null);
+        _in=_bin;
+        _out=_bout;
+    }
+    
+    public StringEndPoint(String encoding)
+    {
+        this();
+        if (encoding!=null)
+            _encoding=encoding;
+    }
+    
+    public void setInput(String s) 
+    {
+        try
+        {
+            byte[] bytes = s.getBytes(_encoding);
+            _bin=new ByteArrayInputStream(bytes);
+            _in=_bin;
+            _bout = new ByteArrayOutputStream();
+            _out=_bout;
+            _ishut=false;
+            _oshut=false;
+        }
+        catch(Exception e)
+        {
+            throw new IllegalStateException(e.toString());
+        }
+    }
+    
+    public String getOutput() 
+    {
+        try
+        {
+            String s = new String(_bout.toByteArray(),_encoding);
+            _bout.reset();
+      	  return s;
+        }
+        catch(final Exception e)
+        {
+            throw new IllegalStateException(_encoding)
+            {
+                {initCause(e);}
+            };
+        }
+    }
+
+    /**
+     * @return <code>true</code> if there are bytes remaining to be read from the encoded input
+     */
+    public boolean hasMore()
+    {
+        return _bin.available()>0;
+    }   
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/nio/AsyncConnection.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,28 @@
+//
+//  ========================================================================
+//  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.io.nio;
+
+import java.io.IOException;
+
+import org.eclipse.jetty.io.Connection;
+
+public interface AsyncConnection extends Connection
+{
+    void onInputShutdown() throws IOException;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/nio/ChannelEndPoint.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,509 @@
+//
+//  ========================================================================
+//  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.io.nio;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketException;
+import java.nio.ByteBuffer;
+import java.nio.channels.ByteChannel;
+import java.nio.channels.GatheringByteChannel;
+import java.nio.channels.SelectableChannel;
+import java.nio.channels.SocketChannel;
+
+import org.eclipse.jetty.io.Buffer;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * Channel End Point.
+ * <p>Holds the channel and socket for an NIO endpoint.
+ *
+ */
+public class ChannelEndPoint implements EndPoint
+{
+    private static final Logger LOG = Log.getLogger(ChannelEndPoint.class);
+
+    protected final ByteChannel _channel;
+    protected final ByteBuffer[] _gather2=new ByteBuffer[2];
+    protected final Socket _socket;
+    protected final InetSocketAddress _local;
+    protected final InetSocketAddress _remote;
+    protected volatile int _maxIdleTime;
+    private volatile boolean _ishut;
+    private volatile boolean _oshut;
+
+    public ChannelEndPoint(ByteChannel channel) throws IOException
+    {
+        super();
+        this._channel = channel;
+        _socket=(channel instanceof SocketChannel)?((SocketChannel)channel).socket():null;
+        if (_socket!=null)
+        {
+            _local=(InetSocketAddress)_socket.getLocalSocketAddress();
+            _remote=(InetSocketAddress)_socket.getRemoteSocketAddress();
+            _maxIdleTime=_socket.getSoTimeout();
+        }
+        else
+        {
+            _local=_remote=null;
+        }
+    }
+
+    protected ChannelEndPoint(ByteChannel channel, int maxIdleTime) throws IOException
+    {
+        this._channel = channel;
+        _maxIdleTime=maxIdleTime;
+        _socket=(channel instanceof SocketChannel)?((SocketChannel)channel).socket():null;
+        if (_socket!=null)
+        {
+            _local=(InetSocketAddress)_socket.getLocalSocketAddress();
+            _remote=(InetSocketAddress)_socket.getRemoteSocketAddress();
+            _socket.setSoTimeout(_maxIdleTime);
+        }
+        else
+        {
+            _local=_remote=null;
+        }
+    }
+
+    public boolean isBlocking()
+    {
+        return  !(_channel instanceof SelectableChannel) || ((SelectableChannel)_channel).isBlocking();
+    }
+
+    public boolean blockReadable(long millisecs) throws IOException
+    {
+        return true;
+    }
+
+    public boolean blockWritable(long millisecs) throws IOException
+    {
+        return true;
+    }
+
+    /*
+     * @see org.eclipse.io.EndPoint#isOpen()
+     */
+    public boolean isOpen()
+    {
+        return _channel.isOpen();
+    }
+
+    /** Shutdown the channel Input.
+     * Cannot be overridden. To override, see {@link #shutdownInput()}
+     * @throws IOException
+     */
+    protected final void shutdownChannelInput() throws IOException
+    {
+        LOG.debug("ishut {}", this);
+        _ishut = true;
+        if (_channel.isOpen())
+        {
+            if (_socket != null)
+            {
+                try
+                {
+                    if (!_socket.isInputShutdown())
+                    {
+                        _socket.shutdownInput();
+                    }
+                }
+                catch (SocketException e)
+                {
+                    LOG.debug(e.toString());
+                    LOG.ignore(e);
+                }
+                finally
+                {
+                    if (_oshut)
+                    {
+                        close();
+                    }
+                }
+            }
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.io.EndPoint#close()
+     */
+    public void shutdownInput() throws IOException
+    {
+        shutdownChannelInput();
+    }
+
+    protected final void shutdownChannelOutput() throws IOException
+    {
+        LOG.debug("oshut {}",this);
+        _oshut = true;
+        if (_channel.isOpen())
+        {
+            if (_socket != null)
+            {
+                try
+                {
+                    if (!_socket.isOutputShutdown())
+                    {
+                        _socket.shutdownOutput();
+                    }
+                }
+                catch (SocketException e)
+                {
+                    LOG.debug(e.toString());
+                    LOG.ignore(e);
+                }
+                finally
+                {
+                    if (_ishut)
+                    {
+                        close();
+                    }
+                }
+            }
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.io.EndPoint#close()
+     */
+    public void shutdownOutput() throws IOException
+    {
+        shutdownChannelOutput();
+    }
+
+    public boolean isOutputShutdown()
+    {
+        return _oshut || !_channel.isOpen() || _socket != null && _socket.isOutputShutdown();
+    }
+
+    public boolean isInputShutdown()
+    {
+        return _ishut || !_channel.isOpen() || _socket != null && _socket.isInputShutdown();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.io.EndPoint#close()
+     */
+    public void close() throws IOException
+    {
+        LOG.debug("close {}",this);
+        _channel.close();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.io.EndPoint#fill(org.eclipse.io.Buffer)
+     */
+    public int fill(Buffer buffer) throws IOException
+    {
+        if (_ishut)
+            return -1;
+        Buffer buf = buffer.buffer();
+        int len=0;
+        if (buf instanceof NIOBuffer)
+        {
+            final NIOBuffer nbuf = (NIOBuffer)buf;
+            final ByteBuffer bbuf=nbuf.getByteBuffer();
+
+            //noinspection SynchronizationOnLocalVariableOrMethodParameter
+            try
+            {
+                synchronized(bbuf)
+                {
+                    try
+                    {
+                        bbuf.position(buffer.putIndex());
+                        len=_channel.read(bbuf);
+                    }
+                    finally
+                    {
+                        buffer.setPutIndex(bbuf.position());
+                        bbuf.position(0);
+                    }
+                }
+
+                if (len<0 && isOpen())
+                {
+                    if (!isInputShutdown())
+                        shutdownInput();
+                    if (isOutputShutdown())
+                        _channel.close();
+                }
+            }
+            catch (IOException x)
+            {
+                LOG.debug("Exception while filling", x);
+                try
+                {
+                    if (_channel.isOpen())
+                        _channel.close();
+                }
+                catch (Exception xx)
+                {
+                    LOG.ignore(xx);
+                }
+
+                if (len>0)
+                    throw x;
+                len=-1;
+            }
+        }
+        else
+        {
+            throw new IOException("Not Implemented");
+        }
+
+        return len;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.io.EndPoint#flush(org.eclipse.io.Buffer)
+     */
+    public int flush(Buffer buffer) throws IOException
+    {
+        Buffer buf = buffer.buffer();
+        int len=0;
+        if (buf instanceof NIOBuffer)
+        {
+            final NIOBuffer nbuf = (NIOBuffer)buf;
+            final ByteBuffer bbuf=nbuf.getByteBuffer().asReadOnlyBuffer();
+            try
+            {
+                bbuf.position(buffer.getIndex());
+                bbuf.limit(buffer.putIndex());
+                len=_channel.write(bbuf);
+            }
+            finally
+            {
+                if (len>0)
+                    buffer.skip(len);
+            }
+        }
+        else if (buf instanceof RandomAccessFileBuffer)
+        {
+            len = ((RandomAccessFileBuffer)buf).writeTo(_channel,buffer.getIndex(),buffer.length());
+            if (len>0)
+                buffer.skip(len);
+        }
+        else if (buffer.array()!=null)
+        {
+            ByteBuffer b = ByteBuffer.wrap(buffer.array(), buffer.getIndex(), buffer.length());
+            len=_channel.write(b);
+            if (len>0)
+                buffer.skip(len);
+        }
+        else
+        {
+            throw new IOException("Not Implemented");
+        }
+        return len;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.io.EndPoint#flush(org.eclipse.io.Buffer, org.eclipse.io.Buffer, org.eclipse.io.Buffer)
+     */
+    public int flush(Buffer header, Buffer buffer, Buffer trailer) throws IOException
+    {
+        int length=0;
+
+        Buffer buf0 = header==null?null:header.buffer();
+        Buffer buf1 = buffer==null?null:buffer.buffer();
+
+        if (_channel instanceof GatheringByteChannel &&
+            header!=null && header.length()!=0 && buf0 instanceof NIOBuffer &&
+            buffer!=null && buffer.length()!=0 && buf1 instanceof NIOBuffer)
+        {
+            length = gatheringFlush(header,((NIOBuffer)buf0).getByteBuffer(),buffer,((NIOBuffer)buf1).getByteBuffer());
+        }
+        else
+        {
+            // flush header
+            if (header!=null && header.length()>0)
+                length=flush(header);
+
+            // flush buffer
+            if ((header==null || header.length()==0) &&
+                 buffer!=null && buffer.length()>0)
+                length+=flush(buffer);
+
+            // flush trailer
+            if ((header==null || header.length()==0) &&
+                (buffer==null || buffer.length()==0) &&
+                 trailer!=null && trailer.length()>0)
+                length+=flush(trailer);
+        }
+
+        return length;
+    }
+
+    protected int gatheringFlush(Buffer header, ByteBuffer bbuf0, Buffer buffer, ByteBuffer bbuf1) throws IOException
+    {
+        int length;
+
+        synchronized(this)
+        {
+            // Adjust position indexs of buf0 and buf1
+            bbuf0=bbuf0.asReadOnlyBuffer();
+            bbuf0.position(header.getIndex());
+            bbuf0.limit(header.putIndex());
+            bbuf1=bbuf1.asReadOnlyBuffer();
+            bbuf1.position(buffer.getIndex());
+            bbuf1.limit(buffer.putIndex());
+
+            _gather2[0]=bbuf0;
+            _gather2[1]=bbuf1;
+
+            // do the gathering write.
+            length=(int)((GatheringByteChannel)_channel).write(_gather2);
+
+            int hl=header.length();
+            if (length>hl)
+            {
+                header.clear();
+                buffer.skip(length-hl);
+            }
+            else if (length>0)
+            {
+                header.skip(length);
+            }
+        }
+        return length;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the channel.
+     */
+    public ByteChannel getChannel()
+    {
+        return _channel;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.io.EndPoint#getLocalAddr()
+     */
+    public String getLocalAddr()
+    {
+        if (_socket==null)
+            return null;
+       if (_local==null || _local.getAddress()==null || _local.getAddress().isAnyLocalAddress())
+           return StringUtil.ALL_INTERFACES;
+        return _local.getAddress().getHostAddress();
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.io.EndPoint#getLocalHost()
+     */
+    public String getLocalHost()
+    {
+        if (_socket==null)
+            return null;
+       if (_local==null || _local.getAddress()==null || _local.getAddress().isAnyLocalAddress())
+           return StringUtil.ALL_INTERFACES;
+        return _local.getAddress().getCanonicalHostName();
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.io.EndPoint#getLocalPort()
+     */
+    public int getLocalPort()
+    {
+        if (_socket==null)
+            return 0;
+        if (_local==null)
+            return -1;
+        return _local.getPort();
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.io.EndPoint#getRemoteAddr()
+     */
+    public String getRemoteAddr()
+    {
+        if (_socket==null)
+            return null;
+        if (_remote==null)
+            return null;
+        return _remote.getAddress().getHostAddress();
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.io.EndPoint#getRemoteHost()
+     */
+    public String getRemoteHost()
+    {
+        if (_socket==null)
+            return null;
+        if (_remote==null)
+            return null;
+        return _remote.getAddress().getCanonicalHostName();
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.io.EndPoint#getRemotePort()
+     */
+    public int getRemotePort()
+    {
+        if (_socket==null)
+            return 0;
+        return _remote==null?-1:_remote.getPort();
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.io.EndPoint#getConnection()
+     */
+    public Object getTransport()
+    {
+        return _channel;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void flush()
+        throws IOException
+    {
+    }
+
+    /* ------------------------------------------------------------ */
+    public int getMaxIdleTime()
+    {
+        return _maxIdleTime;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.io.bio.StreamEndPoint#setMaxIdleTime(int)
+     */
+    public void setMaxIdleTime(int timeMs) throws IOException
+    {
+        if (_socket!=null && timeMs!=_maxIdleTime)
+            _socket.setSoTimeout(timeMs>0?timeMs:0);
+        _maxIdleTime=timeMs;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/nio/DirectNIOBuffer.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,354 @@
+//
+//  ========================================================================
+//  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.io.nio;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.nio.channels.FileChannel;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+
+import org.eclipse.jetty.io.AbstractBuffer;
+import org.eclipse.jetty.io.Buffer;
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/* ------------------------------------------------------------------------------- */
+/** 
+ * 
+ * 
+ */
+public class DirectNIOBuffer extends AbstractBuffer implements NIOBuffer
+{ 	
+    private static final Logger LOG = Log.getLogger(DirectNIOBuffer.class);
+
+    protected final ByteBuffer _buf;
+    private ReadableByteChannel _in;
+    private InputStream _inStream;
+    private WritableByteChannel _out;
+    private OutputStream _outStream;
+
+    public DirectNIOBuffer(int size)
+    {
+        super(READWRITE,NON_VOLATILE);
+        _buf = ByteBuffer.allocateDirect(size);
+        _buf.position(0);
+        _buf.limit(_buf.capacity());
+    }
+    
+    public DirectNIOBuffer(ByteBuffer buffer,boolean immutable)
+    {
+        super(immutable?IMMUTABLE:READWRITE,NON_VOLATILE);
+        if (!buffer.isDirect())
+            throw new IllegalArgumentException();
+        _buf = buffer;
+        setGetIndex(buffer.position());
+        setPutIndex(buffer.limit());
+    }
+
+    /**
+     * @param file
+     */
+    public DirectNIOBuffer(File file) throws IOException
+    {
+        super(READONLY,NON_VOLATILE);
+        FileInputStream fis = null;
+        FileChannel fc = null;
+        try
+        {
+            fis = new FileInputStream(file);
+            fc = fis.getChannel();
+            _buf = fc.map(FileChannel.MapMode.READ_ONLY, 0, file.length());
+            setGetIndex(0);
+            setPutIndex((int)file.length());
+            _access=IMMUTABLE;
+        }
+        finally
+        {
+            if (fc != null) try {fc.close();} catch (IOException e){LOG.ignore(e);}
+            IO.close(fis);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isDirect()
+    {
+        return true;
+    }
+
+    /* ------------------------------------------------------------ */
+    public byte[] array()
+    {
+        return null;
+    }
+
+    /* ------------------------------------------------------------ */
+    public int capacity()
+    {
+        return _buf.capacity();
+    }
+
+    /* ------------------------------------------------------------ */
+    public byte peek(int position)
+    {
+        return _buf.get(position);
+    }
+
+    public int peek(int index, byte[] b, int offset, int length)
+    {
+        int l = length;
+        if (index+l > capacity())
+        {
+            l=capacity()-index;
+            if (l==0)
+                return -1;
+        }
+        
+        if (l < 0) 
+            return -1;
+        try
+        {
+            _buf.position(index);
+            _buf.get(b,offset,l);
+        }
+        finally
+        {
+            _buf.position(0);
+        }
+        
+        return l;
+    }
+
+    public void poke(int index, byte b)
+    {
+        if (isReadOnly()) throw new IllegalStateException(__READONLY);
+        if (index < 0) throw new IllegalArgumentException("index<0: " + index + "<0");
+        if (index > capacity())
+                throw new IllegalArgumentException("index>capacity(): " + index + ">" + capacity());
+        _buf.put(index,b);
+    }
+
+    @Override
+    public int poke(int index, Buffer src)
+    {
+        if (isReadOnly()) throw new IllegalStateException(__READONLY);
+
+        byte[] array=src.array();
+        if (array!=null)
+        {
+            return poke(index,array,src.getIndex(),src.length());
+        }
+        else
+        {
+            Buffer src_buf=src.buffer();
+            if (src_buf instanceof DirectNIOBuffer)
+            {
+                ByteBuffer src_bytebuf = ((DirectNIOBuffer)src_buf)._buf;
+                if (src_bytebuf==_buf)
+                    src_bytebuf=_buf.duplicate();
+                try
+                {   
+                    _buf.position(index);
+                    int space = _buf.remaining();
+                    
+                    int length=src.length();
+                    if (length>space)    
+                        length=space;
+                    
+                    src_bytebuf.position(src.getIndex());
+                    src_bytebuf.limit(src.getIndex()+length);
+                    
+                    _buf.put(src_bytebuf);
+                    return length;
+                }
+                finally
+                {
+                    _buf.position(0);
+                    src_bytebuf.limit(src_bytebuf.capacity());
+                    src_bytebuf.position(0);
+                }
+            }
+            else
+                return super.poke(index,src);
+        }
+    }
+    
+    @Override
+    public int poke(int index, byte[] b, int offset, int length)
+    {
+        if (isReadOnly()) throw new IllegalStateException(__READONLY);
+
+        if (index < 0) throw new IllegalArgumentException("index<0: " + index + "<0");
+
+        if (index + length > capacity())
+        {
+            length=capacity()-index;
+            if (length<0)
+                throw new IllegalArgumentException("index>capacity(): " + index + ">" + capacity());
+        }
+
+        try
+        {
+            _buf.position(index);
+            
+            int space=_buf.remaining();
+            
+            if (length>space)
+                length=space;
+            if (length>0)
+                _buf.put(b,offset,length);
+            return length;
+        }
+        finally
+        {
+            _buf.position(0);
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    public ByteBuffer getByteBuffer()
+    {
+        return _buf;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public int readFrom(InputStream in, int max) throws IOException
+    {
+        if (_in==null || !_in.isOpen() || in!=_inStream)
+        {
+            _in=Channels.newChannel(in);
+            _inStream=in;
+        }
+
+        if (max<0 || max>space())
+            max=space();
+        int p = putIndex();
+        
+        try
+        {
+            int len=0, total=0, available=max;
+            int loop=0;
+            while (total<max) 
+            {
+                _buf.position(p);
+                _buf.limit(p+available);
+                len=_in.read(_buf);
+                if (len<0)
+                {
+                    _in=null;
+                    _inStream=in;
+                    break;
+                }
+                else if (len>0)
+                {
+                    p += len;
+                    total += len;
+                    available -= len;
+                    setPutIndex(p);
+                    loop=0;
+                }
+                else if (loop++>1)
+                    break;
+                if (in.available()<=0)
+                    break;
+            }
+            if (len<0 && total==0)
+                return -1;
+            return total;
+            
+        }
+        catch(IOException e)
+        {
+            _in=null;
+            _inStream=in;
+            throw e;
+        }
+        finally
+        {
+            if (_in!=null && !_in.isOpen())
+            {
+                _in=null;
+                _inStream=in;
+            }
+            _buf.position(0);
+            _buf.limit(_buf.capacity());
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void writeTo(OutputStream out) throws IOException
+    {
+        if (_out==null || !_out.isOpen() || out!=_outStream)
+        {
+            _out=Channels.newChannel(out);
+            _outStream=out;
+        }
+
+        synchronized (_buf)
+        {
+            try
+            {
+                int loop=0;
+                while(hasContent() && _out.isOpen())
+                {
+                    _buf.position(getIndex());
+                    _buf.limit(putIndex());
+                    int len=_out.write(_buf);
+                    if (len<0)
+                        break;
+                    else if (len>0)
+                    {
+                        skip(len);
+                        loop=0;
+                    }
+                    else if (loop++>1)
+                        break;
+                }
+
+            }
+            catch(IOException e)
+            {
+                _out=null;
+                _outStream=null;
+                throw e;
+            }
+            finally
+            {
+                if (_out!=null && !_out.isOpen())
+                {
+                    _out=null;
+                    _outStream=null;
+                }
+                _buf.position(0);
+                _buf.limit(_buf.capacity());
+            }
+        }
+    }
+
+    
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/nio/IndirectNIOBuffer.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,62 @@
+//
+//  ========================================================================
+//  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.io.nio;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.io.ByteArrayBuffer;
+
+public class IndirectNIOBuffer extends ByteArrayBuffer implements NIOBuffer
+{
+    protected final ByteBuffer _buf;
+
+    /* ------------------------------------------------------------ */
+    public IndirectNIOBuffer(int size)
+    {
+        super(size,READWRITE,NON_VOLATILE);
+        _buf = ByteBuffer.wrap(_bytes);
+        _buf.position(0);
+        _buf.limit(_buf.capacity());
+    }
+
+    /* ------------------------------------------------------------ */
+    public IndirectNIOBuffer(ByteBuffer buffer,boolean immutable)
+    {
+        super(buffer.array(),0,0, immutable?IMMUTABLE:READWRITE,NON_VOLATILE);
+        if (buffer.isDirect())
+            throw new IllegalArgumentException();
+        _buf = buffer;
+        _get=buffer.position();
+        _put=buffer.limit();
+        buffer.position(0);
+        buffer.limit(buffer.capacity());
+    }
+    
+    /* ------------------------------------------------------------ */
+    public ByteBuffer getByteBuffer()
+    {
+        return _buf;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isDirect()
+    {
+        return false;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/nio/NIOBuffer.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,37 @@
+//
+//  ========================================================================
+//  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.io.nio;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jetty.io.Buffer;
+
+/* ------------------------------------------------------------------------------- */
+/** 
+ * 
+ * 
+ */
+public interface NIOBuffer extends Buffer
+{
+    /* ------------------------------------------------------------ */
+    public ByteBuffer getByteBuffer();
+
+    /* ------------------------------------------------------------ */
+    public boolean isDirect();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/nio/NetworkTrafficSelectChannelEndPoint.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,148 @@
+//
+//  ========================================================================
+//  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.io.nio;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.SocketChannel;
+import java.util.List;
+
+import org.eclipse.jetty.io.Buffer;
+import org.eclipse.jetty.io.NetworkTrafficListener;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+public class NetworkTrafficSelectChannelEndPoint extends SelectChannelEndPoint
+{
+    private static final Logger LOG = Log.getLogger(NetworkTrafficSelectChannelEndPoint.class);
+
+    private final List<NetworkTrafficListener> listeners;
+
+    public NetworkTrafficSelectChannelEndPoint(SocketChannel channel, SelectorManager.SelectSet selectSet, SelectionKey key, int maxIdleTime, List<NetworkTrafficListener> listeners) throws IOException
+    {
+        super(channel, selectSet, key, maxIdleTime);
+        this.listeners = listeners;
+    }
+
+    @Override
+    public int fill(Buffer buffer) throws IOException
+    {
+        int read = super.fill(buffer);
+        notifyIncoming(buffer, read);
+        return read;
+    }
+
+    @Override
+    public int flush(Buffer buffer) throws IOException
+    {
+        int position = buffer.getIndex();
+        int written = super.flush(buffer);
+        notifyOutgoing(buffer, position, written);
+        return written;
+    }
+
+    @Override
+    protected int gatheringFlush(Buffer header, ByteBuffer bbuf0, Buffer buffer, ByteBuffer bbuf1) throws IOException
+    {
+        int headerPosition = header.getIndex();
+        int headerLength = header.length();
+        int bufferPosition = buffer.getIndex();
+        int written = super.gatheringFlush(header, bbuf0, buffer,bbuf1);
+        notifyOutgoing(header, headerPosition, written > headerLength ? headerLength : written);
+        notifyOutgoing(buffer, bufferPosition, written > headerLength ? written - headerLength : 0);
+        return written;
+    }
+
+    public void notifyOpened()
+    {
+        if (listeners != null && !listeners.isEmpty())
+        {
+            for (NetworkTrafficListener listener : listeners)
+            {
+                try
+                {
+                    listener.opened(_socket);
+                }
+                catch (Exception x)
+                {
+                    LOG.warn(x);
+                }
+            }
+        }
+    }
+
+    public void notifyIncoming(Buffer buffer, int read)
+    {
+        if (listeners != null && !listeners.isEmpty() && read > 0)
+        {
+            for (NetworkTrafficListener listener : listeners)
+            {
+                try
+                {
+                    Buffer view = buffer.asReadOnlyBuffer();
+                    listener.incoming(_socket, view);
+                }
+                catch (Exception x)
+                {
+                    LOG.warn(x);
+                }
+            }
+        }
+    }
+
+    public void notifyOutgoing(Buffer buffer, int position, int written)
+    {
+        if (listeners != null && !listeners.isEmpty() && written > 0)
+        {
+            for (NetworkTrafficListener listener : listeners)
+            {
+                try
+                {
+                    Buffer view = buffer.asReadOnlyBuffer();
+                    view.setGetIndex(position);
+                    view.setPutIndex(position + written);
+                    listener.outgoing(_socket, view);
+                }
+                catch (Exception x)
+                {
+                    LOG.warn(x);
+                }
+            }
+        }
+    }
+
+    public void notifyClosed()
+    {
+        if (listeners != null && !listeners.isEmpty())
+        {
+            for (NetworkTrafficListener listener : listeners)
+            {
+                try
+                {
+                    listener.closed(_socket);
+                }
+                catch (Exception x)
+                {
+                    LOG.warn(x);
+                }
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/nio/RandomAccessFileBuffer.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,196 @@
+//
+//  ========================================================================
+//  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.io.nio;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.channels.FileChannel;
+import java.nio.channels.WritableByteChannel;
+
+import org.eclipse.jetty.io.AbstractBuffer;
+import org.eclipse.jetty.io.Buffer;
+
+public class RandomAccessFileBuffer extends AbstractBuffer implements Buffer
+{
+    final RandomAccessFile _file;
+    final FileChannel _channel;
+    final int _capacity;
+
+    public RandomAccessFileBuffer(File file) 
+        throws FileNotFoundException
+    {
+        super(READWRITE,true);
+        assert file.length()<=Integer.MAX_VALUE;
+        _file = new RandomAccessFile(file,"rw");
+        _channel=_file.getChannel();
+        _capacity=Integer.MAX_VALUE;
+        setGetIndex(0);
+        setPutIndex((int)file.length());
+    }
+    
+    public RandomAccessFileBuffer(File file,int capacity) 
+        throws FileNotFoundException
+    {
+        super(READWRITE,true);
+        assert capacity>=file.length();
+        assert file.length()<=Integer.MAX_VALUE;
+        _capacity=capacity;
+        _file = new RandomAccessFile(file,"rw");
+        _channel=_file.getChannel();
+        setGetIndex(0);
+        setPutIndex((int)file.length());
+    }
+    
+    public RandomAccessFileBuffer(File file,int capacity,int access) 
+        throws FileNotFoundException
+    {
+        super(access,true);
+        assert capacity>=file.length();
+        assert file.length()<=Integer.MAX_VALUE;
+        _capacity=capacity;
+        _file = new RandomAccessFile(file,access==READWRITE?"rw":"r");
+        _channel=_file.getChannel();
+        setGetIndex(0);
+        setPutIndex((int)file.length());
+    }
+
+    public byte[] array()
+    {
+        return null;
+    }
+
+    public int capacity()
+    {
+        return _capacity;
+    }
+
+    @Override
+    public void clear()
+    {
+        try
+        {
+            synchronized (_file)
+            {
+                super.clear();
+                _file.setLength(0);
+            }
+        }
+        catch(Exception e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+
+
+    @Override
+    public byte peek()
+    {
+        synchronized (_file)
+        {
+            try
+            {
+                if (_get!=_file.getFilePointer())
+                    _file.seek(_get);
+                return _file.readByte();
+            }
+            catch(Exception e)
+            {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    public byte peek(int index)
+    {
+        synchronized (_file)
+        {
+            try
+            {
+                _file.seek(index);
+                return _file.readByte();
+            }
+            catch(Exception e)
+            {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    public int peek(int index, byte[] b, int offset, int length)
+    {
+        synchronized (_file)
+        {
+            try
+            {
+                _file.seek(index);
+                return _file.read(b,offset,length);
+            }
+            catch(Exception e)
+            {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    public void poke(int index, byte b)
+    {
+        synchronized (_file)
+        {
+            try
+            {
+                _file.seek(index);
+                _file.writeByte(b);
+            }
+            catch(Exception e)
+            {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    @Override
+    public int poke(int index, byte[] b, int offset, int length)
+    {
+        synchronized (_file)
+        {
+            try
+            {
+                _file.seek(index);
+                _file.write(b,offset,length);
+                return length;
+            }
+            catch(Exception e)
+            {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+    
+    public int writeTo(WritableByteChannel channel,int index, int length)
+        throws IOException
+    {
+        synchronized (_file)
+        {
+            return (int)_channel.transferTo(index,length,channel);
+        }
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/nio/SelectChannelEndPoint.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,868 @@
+//
+//  ========================================================================
+//  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.io.nio;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.SelectableChannel;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.SocketChannel;
+import java.util.Locale;
+
+import org.eclipse.jetty.io.AsyncEndPoint;
+import org.eclipse.jetty.io.Buffer;
+import org.eclipse.jetty.io.ConnectedEndPoint;
+import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.io.EofException;
+import org.eclipse.jetty.io.nio.SelectorManager.SelectSet;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.Timeout.Task;
+
+/* ------------------------------------------------------------ */
+/**
+ * An Endpoint that can be scheduled by {@link SelectorManager}.
+ */
+public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPoint, ConnectedEndPoint
+{
+    public static final Logger LOG=Log.getLogger("org.eclipse.jetty.io.nio");
+
+    private final boolean WORK_AROUND_JVM_BUG_6346658 = System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("win");
+    private final SelectorManager.SelectSet _selectSet;
+    private final SelectorManager _manager;
+    private  SelectionKey _key;
+    private final Runnable _handler = new Runnable()
+        {
+            public void run() { handle(); }
+        };
+
+    /** The desired value for {@link SelectionKey#interestOps()} */
+    private int _interestOps;
+
+    /**
+     * The connection instance is the handler for any IO activity on the endpoint.
+     * There is a different type of connection for HTTP, AJP, WebSocket and
+     * ProxyConnect.   The connection may change for an SCEP as it is upgraded
+     * from HTTP to proxy connect or websocket.
+     */
+    private volatile AsyncConnection _connection;
+
+    private static final int STATE_NEEDS_DISPATCH=-1;
+    private static final int STATE_UNDISPATCHED=0;
+    private static final int STATE_DISPATCHED=1;
+    private static final int STATE_ASYNC=2;
+    private int _state;
+    
+    private boolean _onIdle;
+
+    /** true if the last write operation succeed and wrote all offered bytes */
+    private volatile boolean _writable = true;
+
+
+    /** True if a thread has is blocked in {@link #blockReadable(long)} */
+    private boolean _readBlocked;
+
+    /** True if a thread has is blocked in {@link #blockWritable(long)} */
+    private boolean _writeBlocked;
+
+    /** true if {@link SelectSet#destroyEndPoint(SelectChannelEndPoint)} has not been called */
+    private boolean _open;
+
+    private volatile long _idleTimestamp;
+    private volatile boolean _checkIdle;
+    
+    private boolean _interruptable;
+
+    private boolean _ishut;
+
+    /* ------------------------------------------------------------ */
+    public SelectChannelEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey key, int maxIdleTime)
+        throws IOException
+    {
+        super(channel, maxIdleTime);
+
+        _manager = selectSet.getManager();
+        _selectSet = selectSet;
+        _state=STATE_UNDISPATCHED;
+        _onIdle=false;
+        _open=true;
+        _key = key;
+
+        setCheckForIdle(true);
+    }
+
+    /* ------------------------------------------------------------ */
+    public SelectionKey getSelectionKey()
+    {
+        synchronized (this)
+        {
+            return _key;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public SelectorManager getSelectManager()
+    {
+        return _manager;
+    }
+
+    /* ------------------------------------------------------------ */
+    public Connection getConnection()
+    {
+        return _connection;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setConnection(Connection connection)
+    {
+        Connection old=_connection;
+        _connection=(AsyncConnection)connection;
+        if (old!=null && old!=_connection)
+            _manager.endPointUpgraded(this,old);
+    }
+
+    /* ------------------------------------------------------------ */
+    public long getIdleTimestamp()
+    {
+        return _idleTimestamp;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Called by selectSet to schedule handling
+     *
+     */
+    public void schedule()
+    {
+        synchronized (this)
+        {
+            // If there is no key, then do nothing
+            if (_key == null || !_key.isValid())
+            {
+                _readBlocked=false;
+                _writeBlocked=false;
+                this.notifyAll();
+                return;
+            }
+
+            // If there are threads dispatched reading and writing
+            if (_readBlocked || _writeBlocked)
+            {
+                // assert _dispatched;
+                if (_readBlocked && _key.isReadable())
+                    _readBlocked=false;
+                if (_writeBlocked && _key.isWritable())
+                    _writeBlocked=false;
+
+                // wake them up is as good as a dispatched.
+                this.notifyAll();
+
+                // we are not interested in further selecting
+                _key.interestOps(0);
+                if (_state<STATE_DISPATCHED)
+                    updateKey();
+                return;
+            }
+
+            // Remove writeable op
+            if ((_key.readyOps() & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE && (_key.interestOps() & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE)
+            {
+                // Remove writeable op
+                _interestOps = _key.interestOps() & ~SelectionKey.OP_WRITE;
+                _key.interestOps(_interestOps);
+                _writable = true; // Once writable is in ops, only removed with dispatch.
+            }
+
+            // If dispatched, then deregister interest
+            if (_state>=STATE_DISPATCHED)
+                _key.interestOps(0);
+            else
+            {
+                // other wise do the dispatch
+                dispatch();
+                if (_state>=STATE_DISPATCHED && !_selectSet.getManager().isDeferringInterestedOps0())
+                {
+                    _key.interestOps(0);
+                }
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public void asyncDispatch()
+    {
+        synchronized(this)
+        {
+            switch(_state)
+            {
+                case STATE_NEEDS_DISPATCH:
+                case STATE_UNDISPATCHED:
+                    dispatch();
+                    break;
+                    
+                case STATE_DISPATCHED:
+                case STATE_ASYNC:
+                    _state=STATE_ASYNC;
+                    break;
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public void dispatch()
+    {
+        synchronized(this)
+        {
+            if (_state<=STATE_UNDISPATCHED)
+            {
+                if (_onIdle)
+                    _state = STATE_NEEDS_DISPATCH;
+                else
+                {
+                    _state = STATE_DISPATCHED;
+                    boolean dispatched = _manager.dispatch(_handler);
+                    if(!dispatched)
+                    {
+                        _state = STATE_NEEDS_DISPATCH;
+                        LOG.warn("Dispatched Failed! "+this+" to "+_manager);
+                        updateKey();
+                    }
+                }
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Called when a dispatched thread is no longer handling the endpoint.
+     * The selection key operations are updated.
+     * @return If false is returned, the endpoint has been redispatched and
+     * thread must keep handling the endpoint.
+     */
+    protected boolean undispatch()
+    {
+        synchronized (this)
+        {
+            switch(_state)
+            {
+                case STATE_ASYNC:
+                    _state=STATE_DISPATCHED;
+                    return false;
+
+                default:
+                    _state=STATE_UNDISPATCHED;
+                    updateKey();
+                    return true;
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public void cancelTimeout(Task task)
+    {
+        getSelectSet().cancelTimeout(task);
+    }
+
+    /* ------------------------------------------------------------ */
+    public void scheduleTimeout(Task task, long timeoutMs)
+    {
+        getSelectSet().scheduleTimeout(task,timeoutMs);
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setCheckForIdle(boolean check)
+    {
+        if (check)
+        {
+            _idleTimestamp=System.currentTimeMillis();
+            _checkIdle=true;
+        }
+        else
+            _checkIdle=false;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isCheckForIdle()
+    {
+        return _checkIdle;
+    }
+
+    /* ------------------------------------------------------------ */
+    protected void notIdle()
+    {
+        _idleTimestamp=System.currentTimeMillis();
+    }
+
+    /* ------------------------------------------------------------ */
+    public void checkIdleTimestamp(long now)
+    {
+        if (isCheckForIdle() && _maxIdleTime>0)
+        {
+            final long idleForMs=now-_idleTimestamp;
+
+            if (idleForMs>_maxIdleTime)
+            {
+                // Don't idle out again until onIdleExpired task completes.
+                setCheckForIdle(false);
+                _manager.dispatch(new Runnable()
+                {
+                    public void run()
+                    {
+                        try
+                        {
+                            onIdleExpired(idleForMs);
+                        }
+                        finally
+                        {
+                            setCheckForIdle(true);
+                        }
+                    }
+                });
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public void onIdleExpired(long idleForMs)
+    {
+        try
+        {
+            synchronized (this)
+            {
+                _onIdle=true;
+            }
+
+            _connection.onIdleExpired(idleForMs);
+        }
+        finally
+        {
+            synchronized (this)
+            {
+                _onIdle=false;
+                if (_state==STATE_NEEDS_DISPATCH)
+                    dispatch();
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public int fill(Buffer buffer) throws IOException
+    {
+        int fill=super.fill(buffer);
+        if (fill>0)
+            notIdle();
+        return fill;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public int flush(Buffer header, Buffer buffer, Buffer trailer) throws IOException
+    {
+        int l = super.flush(header, buffer, trailer);
+
+        // If there was something to write and it wasn't written, then we are not writable.
+        if (l==0 && ( header!=null && header.hasContent() || buffer!=null && buffer.hasContent() || trailer!=null && trailer.hasContent()))
+        {
+            synchronized (this)
+            {   
+                _writable=false;
+                if (_state<STATE_DISPATCHED)
+                    updateKey();
+            }
+        }
+        else if (l>0)
+        {
+            _writable=true;
+            notIdle();
+        }
+        return l;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     */
+    @Override
+    public int flush(Buffer buffer) throws IOException
+    {
+        int l = super.flush(buffer);
+
+        // If there was something to write and it wasn't written, then we are not writable.
+        if (l==0 && buffer!=null && buffer.hasContent())
+        {
+            synchronized (this)
+            {   
+                _writable=false;
+                if (_state<STATE_DISPATCHED)
+                    updateKey();
+            }
+        }
+        else if (l>0)
+        {
+            _writable=true;
+            notIdle();
+        }
+
+        return l;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * Allows thread to block waiting for further events.
+     */
+    @Override
+    public boolean blockReadable(long timeoutMs) throws IOException
+    {
+        synchronized (this)
+        {
+            if (isInputShutdown())
+                throw new EofException();
+
+            long now=_selectSet.getNow();
+            long end=now+timeoutMs;
+            boolean check=isCheckForIdle();
+            setCheckForIdle(true);
+            try
+            {
+                _readBlocked=true;
+                while (!isInputShutdown() && _readBlocked)
+                {
+                    try
+                    {
+                        updateKey();
+                        this.wait(timeoutMs>0?(end-now):10000);
+                    }
+                    catch (final InterruptedException e)
+                    {
+                        LOG.warn(e);
+                        if (_interruptable)
+                            throw new InterruptedIOException(){{this.initCause(e);}};
+                    }
+                    finally
+                    {
+                        now=_selectSet.getNow();
+                    }
+
+                    if (_readBlocked && timeoutMs>0 && now>=end)
+                        return false;
+                }
+            }
+            finally
+            {
+                _readBlocked=false;
+                setCheckForIdle(check);
+            }
+        }
+        return true;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * Allows thread to block waiting for further events.
+     */
+    @Override
+    public boolean blockWritable(long timeoutMs) throws IOException
+    {
+        synchronized (this)
+        {
+            if (isOutputShutdown())
+                throw new EofException();
+
+            long now=_selectSet.getNow();
+            long end=now+timeoutMs;
+            boolean check=isCheckForIdle();
+            setCheckForIdle(true);
+            try
+            {
+                _writeBlocked=true;
+                while (_writeBlocked && !isOutputShutdown())
+                {
+                    try
+                    {
+                        updateKey();
+                        this.wait(timeoutMs>0?(end-now):10000);
+                    }
+                    catch (final InterruptedException e)
+                    {
+                        LOG.warn(e);
+                        if (_interruptable)
+                            throw new InterruptedIOException(){{this.initCause(e);}};
+                    }
+                    finally
+                    {
+                        now=_selectSet.getNow();
+                    }
+                    if (_writeBlocked && timeoutMs>0 && now>=end)
+                        return false;
+                }
+            }
+            finally
+            {
+                _writeBlocked=false;
+                setCheckForIdle(check);
+            }
+        }
+        return true;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set the interruptable mode of the endpoint.
+     * If set to false (default), then interrupts are assumed to be spurious 
+     * and blocking operations continue unless the endpoint has been closed.
+     * If true, then interrupts of blocking operations result in InterruptedIOExceptions
+     * being thrown.
+     * @param interupable
+     */
+    public void setInterruptable(boolean interupable)
+    {
+        synchronized (this)
+        {
+            _interruptable=interupable;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isInterruptable()
+    {
+        return _interruptable;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.io.AsyncEndPoint#scheduleWrite()
+     */
+    public void scheduleWrite()
+    {
+        if (_writable)
+            LOG.debug("Required scheduleWrite {}",this);
+
+        _writable=false;
+        updateKey();
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isWritable()
+    {
+        return _writable;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean hasProgressed()
+    {
+        return false;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Updates selection key. Adds operations types to the selection key as needed. No operations
+     * are removed as this is only done during dispatch. This method records the new key and
+     * schedules a call to doUpdateKey to do the keyChange
+     */
+    private void updateKey()
+    {
+        final boolean changed;
+        synchronized (this)
+        {
+            int current_ops=-1;
+            if (getChannel().isOpen())
+            {
+                boolean read_interest = _readBlocked || (_state<STATE_DISPATCHED && !_connection.isSuspended());
+                boolean write_interest= _writeBlocked || (_state<STATE_DISPATCHED && !_writable);
+
+                _interestOps =
+                    ((!_socket.isInputShutdown() && read_interest ) ? SelectionKey.OP_READ  : 0)
+                |   ((!_socket.isOutputShutdown()&& write_interest) ? SelectionKey.OP_WRITE : 0);
+                try
+                {
+                    current_ops = ((_key!=null && _key.isValid())?_key.interestOps():-1);
+                }
+                catch(Exception e)
+                {
+                    _key=null;
+                    LOG.ignore(e);
+                }
+            }
+            changed=_interestOps!=current_ops;
+        }
+
+        if(changed)
+        {
+            _selectSet.addChange(this);
+            _selectSet.wakeup();
+        }
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Synchronize the interestOps with the actual key. Call is scheduled by a call to updateKey
+     */
+    void doUpdateKey()
+    {
+        synchronized (this)
+        {
+            if (getChannel().isOpen())
+            {
+                if (_interestOps>0)
+                {
+                    if (_key==null || !_key.isValid())
+                    {
+                        SelectableChannel sc = (SelectableChannel)getChannel();
+                        if (sc.isRegistered())
+                        {
+                            updateKey();
+                        }
+                        else
+                        {
+                            try
+                            {
+                                _key=((SelectableChannel)getChannel()).register(_selectSet.getSelector(),_interestOps,this);
+                            }
+                            catch (Exception e)
+                            {
+                                LOG.ignore(e);
+                                if (_key!=null && _key.isValid())
+                                {
+                                    _key.cancel();
+                                }
+
+                                if (_open)
+                                {
+                                    _selectSet.destroyEndPoint(this);
+                                }
+                                _open=false;
+                                _key = null;
+                            }
+                        }
+                    }
+                    else
+                    {
+                        _key.interestOps(_interestOps);
+                    }
+                }
+                else
+                {
+                    if (_key!=null && _key.isValid())
+                        _key.interestOps(0);
+                    else
+                        _key=null;
+                }
+            }
+            else
+            {
+                if (_key!=null && _key.isValid())
+                    _key.cancel();
+
+                if (_open)
+                {
+                    _open=false;
+                    _selectSet.destroyEndPoint(this);
+                }
+                _key = null;
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     */
+    protected void handle()
+    {
+        boolean dispatched=true;
+        try
+        {
+            while(dispatched)
+            {
+                try
+                {
+                    while(true)
+                    {
+                        final AsyncConnection next = (AsyncConnection)_connection.handle();
+                        if (next!=_connection)
+                        {
+                            LOG.debug("{} replaced {}",next,_connection);
+                            Connection old=_connection;
+                            _connection=next;
+                            _manager.endPointUpgraded(this,old);
+                            continue;
+                        }
+                        break;
+                    }
+                }
+                catch (ClosedChannelException e)
+                {
+                    LOG.ignore(e);
+                }
+                catch (EofException e)
+                {
+                    LOG.debug("EOF", e);
+                    try{close();}
+                    catch(IOException e2){LOG.ignore(e2);}
+                }
+                catch (IOException e)
+                {
+                    LOG.warn(e.toString());
+                    try{close();}
+                    catch(IOException e2){LOG.ignore(e2);}
+                }
+                catch (Throwable e)
+                {
+                    LOG.warn("handle failed", e);
+                    try{close();}
+                    catch(IOException e2){LOG.ignore(e2);}
+                }
+                finally
+                {
+                    if (!_ishut && isInputShutdown() && isOpen())
+                    {
+                        _ishut=true;
+                        try
+                        {
+                            _connection.onInputShutdown();
+                        }
+                        catch(Throwable x)
+                        {
+                            LOG.warn("onInputShutdown failed", x);
+                            try{close();}
+                            catch(IOException e2){LOG.ignore(e2);}
+                        }
+                        finally
+                        {
+                            updateKey();
+                        }
+                    }
+                    dispatched=!undispatch();
+                }
+            }
+        }
+        finally
+        {
+            if (dispatched)
+            {
+                dispatched=!undispatch();
+                while (dispatched)
+                {
+                    LOG.warn("SCEP.run() finally DISPATCHED");
+                    dispatched=!undispatch();
+                }
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.io.nio.ChannelEndPoint#close()
+     */
+    @Override
+    public void close() throws IOException
+    {
+        // On unix systems there is a JVM issue that if you cancel before closing, it can 
+        // cause the selector to block waiting for a channel to close and that channel can 
+        // block waiting for the remote end.  But on windows, if you don't cancel before a 
+        // close, then the selector can block anyway!
+        // https://bugs.eclipse.org/bugs/show_bug.cgi?id=357318
+        if (WORK_AROUND_JVM_BUG_6346658)
+        {
+            try
+            {
+                SelectionKey key = _key;
+                if (key!=null)
+                    key.cancel();
+            }
+            catch (Throwable e)
+            {
+                LOG.ignore(e);
+            }
+        }
+
+        try
+        {
+            super.close();
+        }
+        catch (IOException e)
+        {
+            LOG.ignore(e);
+        }
+        finally
+        {
+            updateKey();
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public String toString()
+    {
+        // Do NOT use synchronized (this)
+        // because it's very easy to deadlock when debugging is enabled.
+        // We do a best effort to print the right toString() and that's it.
+        SelectionKey key = _key;
+        String keyString = "";
+        if (key != null)
+        {
+            if (key.isValid())
+            {
+                if (key.isReadable())
+                    keyString += "r";
+                if (key.isWritable())
+                    keyString += "w";
+            }
+            else
+            {
+                keyString += "!";
+            }
+        }
+        else
+        {
+            keyString += "-";
+        }
+        return String.format("SCEP@%x{l(%s)<->r(%s),s=%d,open=%b,ishut=%b,oshut=%b,rb=%b,wb=%b,w=%b,i=%d%s}-{%s}",
+                hashCode(),
+                _socket.getRemoteSocketAddress(),
+                _socket.getLocalSocketAddress(),
+                _state,
+                isOpen(),
+                isInputShutdown(),
+                isOutputShutdown(),
+                _readBlocked,
+                _writeBlocked,
+                _writable,
+                _interestOps,
+                keyString,
+                _connection);
+    }
+
+    /* ------------------------------------------------------------ */
+    public SelectSet getSelectSet()
+    {
+        return _selectSet;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Don't set the SoTimeout
+     * @see org.eclipse.jetty.io.nio.ChannelEndPoint#setMaxIdleTime(int)
+     */
+    @Override
+    public void setMaxIdleTime(int timeMs) throws IOException
+    {
+        _maxIdleTime=timeMs;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/nio/SelectorManager.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,1034 @@
+//
+//  ========================================================================
+//  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.io.nio;
+
+import java.io.IOException;
+import java.nio.channels.CancelledKeyException;
+import java.nio.channels.Channel;
+import java.nio.channels.ClosedSelectorException;
+import java.nio.channels.SelectableChannel;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.io.AsyncEndPoint;
+import org.eclipse.jetty.io.ConnectedEndPoint;
+import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.util.TypeUtil;
+import org.eclipse.jetty.util.component.AbstractLifeCycle;
+import org.eclipse.jetty.util.component.AggregateLifeCycle;
+import org.eclipse.jetty.util.component.Dumpable;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.Timeout;
+import org.eclipse.jetty.util.thread.Timeout.Task;
+
+
+/* ------------------------------------------------------------ */
+/**
+ * The Selector Manager manages and number of SelectSets to allow
+ * NIO scheduling to scale to large numbers of connections.
+ * <p>
+ */
+public abstract class SelectorManager extends AbstractLifeCycle implements Dumpable
+{
+    public static final Logger LOG=Log.getLogger("org.eclipse.jetty.io.nio");
+
+    private static final int __MONITOR_PERIOD=Integer.getInteger("org.eclipse.jetty.io.nio.MONITOR_PERIOD",1000).intValue();
+    private static final int __MAX_SELECTS=Integer.getInteger("org.eclipse.jetty.io.nio.MAX_SELECTS",100000).intValue();
+    private static final int __BUSY_PAUSE=Integer.getInteger("org.eclipse.jetty.io.nio.BUSY_PAUSE",50).intValue();
+    private static final int __IDLE_TICK=Integer.getInteger("org.eclipse.jetty.io.nio.IDLE_TICK",400).intValue();
+
+    private int _maxIdleTime;
+    private int _lowResourcesMaxIdleTime;
+    private long _lowResourcesConnections;
+    private SelectSet[] _selectSet;
+    private int _selectSets=1;
+    private volatile int _set=0;
+    private boolean _deferringInterestedOps0=true;
+    private int _selectorPriorityDelta=0;
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param maxIdleTime The maximum period in milli seconds that a connection may be idle before it is closed.
+     * @see #setLowResourcesMaxIdleTime(long)
+     */
+    public void setMaxIdleTime(long maxIdleTime)
+    {
+        _maxIdleTime=(int)maxIdleTime;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param selectSets number of select sets to create
+     */
+    public void setSelectSets(int selectSets)
+    {
+        long lrc = _lowResourcesConnections * _selectSets;
+        _selectSets=selectSets;
+        _lowResourcesConnections=lrc/_selectSets;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return the max idle time
+     */
+    public long getMaxIdleTime()
+    {
+        return _maxIdleTime;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return the number of select sets in use
+     */
+    public int getSelectSets()
+    {
+        return _selectSets;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param i
+     * @return The select set
+     */
+    public SelectSet getSelectSet(int i)
+    {
+        return _selectSet[i];
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Register a channel
+     * @param channel
+     * @param att Attached Object
+     */
+    public void register(SocketChannel channel, Object att)
+    {
+        // The ++ increment here is not atomic, but it does not matter.
+        // so long as the value changes sometimes, then connections will
+        // be distributed over the available sets.
+
+        int s=_set++;
+        if (s<0)
+            s=-s;
+        s=s%_selectSets;
+        SelectSet[] sets=_selectSet;
+        if (sets!=null)
+        {
+            SelectSet set=sets[s];
+            set.addChange(channel,att);
+            set.wakeup();
+        }
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /** Register a channel
+     * @param channel
+     */
+    public void register(SocketChannel channel)
+    {
+        // The ++ increment here is not atomic, but it does not matter.
+        // so long as the value changes sometimes, then connections will
+        // be distributed over the available sets.
+
+        int s=_set++;
+        if (s<0)
+            s=-s;
+        s=s%_selectSets;
+        SelectSet[] sets=_selectSet;
+        if (sets!=null)
+        {
+            SelectSet set=sets[s];
+            set.addChange(channel);
+            set.wakeup();
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Register a {@link ServerSocketChannel}
+     * @param acceptChannel
+     */
+    public void register(ServerSocketChannel acceptChannel)
+    {
+        int s=_set++;
+        if (s<0)
+            s=-s;
+        s=s%_selectSets;
+        SelectSet set=_selectSet[s];
+        set.addChange(acceptChannel);
+        set.wakeup();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return delta The value to add to the selector thread priority.
+     */
+    public int getSelectorPriorityDelta()
+    {
+        return _selectorPriorityDelta;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set the selector thread priorty delta.
+     * @param delta The value to add to the selector thread priority.
+     */
+    public void setSelectorPriorityDelta(int delta)
+    {
+        _selectorPriorityDelta=delta;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return the lowResourcesConnections
+     */
+    public long getLowResourcesConnections()
+    {
+        return _lowResourcesConnections*_selectSets;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set the number of connections, which if exceeded places this manager in low resources state.
+     * This is not an exact measure as the connection count is averaged over the select sets.
+     * @param lowResourcesConnections the number of connections
+     * @see #setLowResourcesMaxIdleTime(long)
+     */
+    public void setLowResourcesConnections(long lowResourcesConnections)
+    {
+        _lowResourcesConnections=(lowResourcesConnections+_selectSets-1)/_selectSets;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return the lowResourcesMaxIdleTime
+     */
+    public long getLowResourcesMaxIdleTime()
+    {
+        return _lowResourcesMaxIdleTime;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param lowResourcesMaxIdleTime the period in ms that a connection is allowed to be idle when this SelectSet has more connections than {@link #getLowResourcesConnections()}
+     * @see #setMaxIdleTime(long)
+     */
+    public void setLowResourcesMaxIdleTime(long lowResourcesMaxIdleTime)
+    {
+        _lowResourcesMaxIdleTime=(int)lowResourcesMaxIdleTime;
+    }
+
+
+    /* ------------------------------------------------------------------------------- */
+    public abstract boolean dispatch(Runnable task);
+
+    /* ------------------------------------------------------------ */
+    /* (non-Javadoc)
+     * @see org.eclipse.component.AbstractLifeCycle#doStart()
+     */
+    @Override
+    protected void doStart() throws Exception
+    {
+        _selectSet = new SelectSet[_selectSets];
+        for (int i=0;i<_selectSet.length;i++)
+            _selectSet[i]= new SelectSet(i);
+
+        super.doStart();
+
+        // start a thread to Select
+        for (int i=0;i<getSelectSets();i++)
+        {
+            final int id=i;
+            boolean selecting=dispatch(new Runnable()
+            {
+                public void run()
+                {
+                    String name=Thread.currentThread().getName();
+                    int priority=Thread.currentThread().getPriority();
+                    try
+                    {
+                        SelectSet[] sets=_selectSet;
+                        if (sets==null)
+                            return;
+                        SelectSet set=sets[id];
+
+                        Thread.currentThread().setName(name+" Selector"+id);
+                        if (getSelectorPriorityDelta()!=0)
+                            Thread.currentThread().setPriority(Thread.currentThread().getPriority()+getSelectorPriorityDelta());
+                        LOG.debug("Starting {} on {}",Thread.currentThread(),this);
+                        while (isRunning())
+                        {
+                            try
+                            {
+                                set.doSelect();
+                            }
+                            catch(IOException e)
+                            {
+                                LOG.ignore(e);
+                            }
+                            catch(Exception e)
+                            {
+                                LOG.warn(e);
+                            }
+                        }
+                    }
+                    finally
+                    {
+                        LOG.debug("Stopped {} on {}",Thread.currentThread(),this);
+                        Thread.currentThread().setName(name);
+                        if (getSelectorPriorityDelta()!=0)
+                            Thread.currentThread().setPriority(priority);
+                    }
+                }
+
+            });
+
+            if (!selecting)
+                throw new IllegalStateException("!Selecting");
+        }
+    }
+
+
+    /* ------------------------------------------------------------------------------- */
+    @Override
+    protected void doStop() throws Exception
+    {
+        SelectSet[] sets= _selectSet;
+        _selectSet=null;
+        if (sets!=null)
+        {
+            for (SelectSet set : sets)
+            {
+                if (set!=null)
+                    set.stop();
+            }
+        }
+        super.doStop();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param endpoint
+     */
+    protected abstract void endPointClosed(SelectChannelEndPoint endpoint);
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param endpoint
+     */
+    protected abstract void endPointOpened(SelectChannelEndPoint endpoint);
+
+    /* ------------------------------------------------------------ */
+    protected abstract void endPointUpgraded(ConnectedEndPoint endpoint,Connection oldConnection);
+
+    /* ------------------------------------------------------------------------------- */
+    public abstract AsyncConnection newConnection(SocketChannel channel, AsyncEndPoint endpoint, Object attachment);
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Create a new end point
+     * @param channel
+     * @param selectSet
+     * @param sKey the selection key
+     * @return the new endpoint {@link SelectChannelEndPoint}
+     * @throws IOException
+     */
+    protected abstract SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectorManager.SelectSet selectSet, SelectionKey sKey) throws IOException;
+
+    /* ------------------------------------------------------------------------------- */
+    protected void connectionFailed(SocketChannel channel,Throwable ex,Object attachment)
+    {
+        LOG.warn(ex+","+channel+","+attachment);
+        LOG.debug(ex);
+    }
+
+    /* ------------------------------------------------------------ */
+    public String dump()
+    {
+        return AggregateLifeCycle.dump(this);
+    }
+
+    /* ------------------------------------------------------------ */
+    public void dump(Appendable out, String indent) throws IOException
+    {
+        AggregateLifeCycle.dumpObject(out,this);
+        AggregateLifeCycle.dump(out,indent,TypeUtil.asList(_selectSet));
+    }
+
+
+    /* ------------------------------------------------------------------------------- */
+    /* ------------------------------------------------------------------------------- */
+    /* ------------------------------------------------------------------------------- */
+    public class SelectSet implements Dumpable
+    {
+        private final int _setID;
+        private final Timeout _timeout;
+
+        private final ConcurrentLinkedQueue<Object> _changes = new ConcurrentLinkedQueue<Object>();
+
+        private volatile Selector _selector;
+
+        private volatile Thread _selecting;
+        private int _busySelects;
+        private long _monitorNext;
+        private boolean _pausing;
+        private boolean _paused;
+        private volatile long _idleTick;
+        private ConcurrentMap<SelectChannelEndPoint,Object> _endPoints = new ConcurrentHashMap<SelectChannelEndPoint, Object>();
+
+        /* ------------------------------------------------------------ */
+        SelectSet(int acceptorID) throws Exception
+        {
+            _setID=acceptorID;
+
+            _idleTick = System.currentTimeMillis();
+            _timeout = new Timeout(this);
+            _timeout.setDuration(0L);
+
+            // create a selector;
+            _selector = Selector.open();
+            _monitorNext=System.currentTimeMillis()+__MONITOR_PERIOD;
+        }
+
+        /* ------------------------------------------------------------ */
+        public void addChange(Object change)
+        {
+            _changes.add(change);
+        }
+
+        /* ------------------------------------------------------------ */
+        public void addChange(SelectableChannel channel, Object att)
+        {
+            if (att==null)
+                addChange(channel);
+            else if (att instanceof EndPoint)
+                addChange(att);
+            else
+                addChange(new ChannelAndAttachment(channel,att));
+        }
+
+        /* ------------------------------------------------------------ */
+        /**
+         * Select and dispatch tasks found from changes and the selector.
+         *
+         * @throws IOException
+         */
+        public void doSelect() throws IOException
+        {
+            try
+            {
+                _selecting=Thread.currentThread();
+                final Selector selector=_selector;
+                // Stopped concurrently ?
+                if (selector == null)
+                    return;
+
+                // Make any key changes required
+                Object change;
+                int changes=_changes.size();
+                while (changes-->0 && (change=_changes.poll())!=null)
+                {
+                    Channel ch=null;
+                    SelectionKey key=null;
+
+                    try
+                    {
+                        if (change instanceof EndPoint)
+                        {
+                            // Update the operations for a key.
+                            SelectChannelEndPoint endpoint = (SelectChannelEndPoint)change;
+                            ch=endpoint.getChannel();
+                            endpoint.doUpdateKey();
+                        }
+                        else if (change instanceof ChannelAndAttachment)
+                        {
+                            // finish accepting/connecting this connection
+                            final ChannelAndAttachment asc = (ChannelAndAttachment)change;
+                            final SelectableChannel channel=asc._channel;
+                            ch=channel;
+                            final Object att = asc._attachment;
+
+                            if ((channel instanceof SocketChannel) && ((SocketChannel)channel).isConnected())
+                            {
+                                key = channel.register(selector,SelectionKey.OP_READ,att);
+                                SelectChannelEndPoint endpoint = createEndPoint((SocketChannel)channel,key);
+                                key.attach(endpoint);
+                                endpoint.schedule();
+                            }
+                            else if (channel.isOpen())
+                            {
+                                key = channel.register(selector,SelectionKey.OP_CONNECT,att);
+                            }
+                        }
+                        else if (change instanceof SocketChannel)
+                        {
+                            // Newly registered channel
+                            final SocketChannel channel=(SocketChannel)change;
+                            ch=channel;
+                            key = channel.register(selector,SelectionKey.OP_READ,null);
+                            SelectChannelEndPoint endpoint = createEndPoint(channel,key);
+                            key.attach(endpoint);
+                            endpoint.schedule();
+                        }
+                        else if (change instanceof ChangeTask)
+                        {
+                            ((Runnable)change).run();
+                        }
+                        else if (change instanceof Runnable)
+                        {
+                            dispatch((Runnable)change);
+                        }
+                        else
+                            throw new IllegalArgumentException(change.toString());
+                    }
+                    catch (CancelledKeyException e)
+                    {
+                        LOG.ignore(e);
+                    }
+                    catch (Throwable e)
+                    {
+                        if (isRunning())
+                            LOG.warn(e);
+                        else
+                            LOG.debug(e);
+
+                        try
+                        {
+                            if (ch!=null)
+                                ch.close();
+                        }
+                        catch(IOException e2)
+                        {
+                            LOG.debug(e2);
+                        }
+                    }
+                }
+
+
+                // Do and instant select to see if any connections can be handled.
+                int selected=selector.selectNow();
+
+                long now=System.currentTimeMillis();
+
+                // if no immediate things to do
+                if (selected==0 && selector.selectedKeys().isEmpty())
+                {
+                    // If we are in pausing mode
+                    if (_pausing)
+                    {
+                        try
+                        {
+                            Thread.sleep(__BUSY_PAUSE); // pause to reduce impact of  busy loop
+                        }
+                        catch(InterruptedException e)
+                        {
+                            LOG.ignore(e);
+                        }
+                        now=System.currentTimeMillis();
+                    }
+
+                    // workout how long to wait in select
+                    _timeout.setNow(now);
+                    long to_next_timeout=_timeout.getTimeToNext();
+
+                    long wait = _changes.size()==0?__IDLE_TICK:0L;
+                    if (wait > 0 && to_next_timeout >= 0 && wait > to_next_timeout)
+                        wait = to_next_timeout;
+
+                    // If we should wait with a select
+                    if (wait>0)
+                    {
+                        long before=now;
+                        selector.select(wait);
+                        now = System.currentTimeMillis();
+                        _timeout.setNow(now);
+
+                        // If we are monitoring for busy selector
+                        // and this select did not wait more than 1ms
+                        if (__MONITOR_PERIOD>0 && now-before <=1)
+                        {
+                            // count this as a busy select and if there have been too many this monitor cycle
+                            if (++_busySelects>__MAX_SELECTS)
+                            {
+                                // Start injecting pauses
+                                _pausing=true;
+
+                                // if this is the first pause
+                                if (!_paused)
+                                {
+                                    // Log and dump some status
+                                    _paused=true;
+                                    LOG.warn("Selector {} is too busy, pausing!",this);
+                                }
+                            }
+                        }
+                    }
+                }
+
+                // have we been destroyed while sleeping
+                if (_selector==null || !selector.isOpen())
+                    return;
+
+                // Look for things to do
+                for (SelectionKey key: selector.selectedKeys())
+                {
+                    SocketChannel channel=null;
+
+                    try
+                    {
+                        if (!key.isValid())
+                        {
+                            key.cancel();
+                            SelectChannelEndPoint endpoint = (SelectChannelEndPoint)key.attachment();
+                            if (endpoint != null)
+                                endpoint.doUpdateKey();
+                            continue;
+                        }
+
+                        Object att = key.attachment();
+                        if (att instanceof SelectChannelEndPoint)
+                        {
+                            if (key.isReadable()||key.isWritable())
+                                ((SelectChannelEndPoint)att).schedule();
+                        }
+                        else if (key.isConnectable())
+                        {
+                            // Complete a connection of a registered channel
+                            channel = (SocketChannel)key.channel();
+                            boolean connected=false;
+                            try
+                            {
+                                connected=channel.finishConnect();
+                            }
+                            catch(Exception e)
+                            {
+                                connectionFailed(channel,e,att);
+                            }
+                            finally
+                            {
+                                if (connected)
+                                {
+                                    key.interestOps(SelectionKey.OP_READ);
+                                    SelectChannelEndPoint endpoint = createEndPoint(channel,key);
+                                    key.attach(endpoint);
+                                    endpoint.schedule();
+                                }
+                                else
+                                {
+                                    key.cancel();
+                                    channel.close();
+                                }
+                            }
+                        }
+                        else
+                        {
+                            // Wrap readable registered channel in an endpoint
+                            channel = (SocketChannel)key.channel();
+                            SelectChannelEndPoint endpoint = createEndPoint(channel,key);
+                            key.attach(endpoint);
+                            if (key.isReadable())
+                                endpoint.schedule();
+                        }
+                        key = null;
+                    }
+                    catch (CancelledKeyException e)
+                    {
+                        LOG.ignore(e);
+                    }
+                    catch (Exception e)
+                    {
+                        if (isRunning())
+                            LOG.warn(e);
+                        else
+                            LOG.ignore(e);
+
+                        try
+                        {
+                            if (channel!=null)
+                                channel.close();
+                        }
+                        catch(IOException e2)
+                        {
+                            LOG.debug(e2);
+                        }
+
+                        if (key != null && !(key.channel() instanceof ServerSocketChannel) && key.isValid())
+                            key.cancel();
+                    }
+                }
+
+                // Everything always handled
+                selector.selectedKeys().clear();
+
+                now=System.currentTimeMillis();
+                _timeout.setNow(now);
+                Task task = _timeout.expired();
+                while (task!=null)
+                {
+                    if (task instanceof Runnable)
+                        dispatch((Runnable)task);
+                    task = _timeout.expired();
+                }
+
+                // Idle tick
+                if (now-_idleTick>__IDLE_TICK)
+                {
+                    _idleTick=now;
+
+                    final long idle_now=((_lowResourcesConnections>0 && selector.keys().size()>_lowResourcesConnections))
+                        ?(now+_maxIdleTime-_lowResourcesMaxIdleTime)
+                        :now;
+
+                    dispatch(new Runnable()
+                    {
+                        public void run()
+                        {
+                            for (SelectChannelEndPoint endp:_endPoints.keySet())
+                            {
+                                endp.checkIdleTimestamp(idle_now);
+                            }
+                        }
+                        public String toString() {return "Idle-"+super.toString();}
+                    });
+
+                }
+
+                // Reset busy select monitor counts
+                if (__MONITOR_PERIOD>0 && now>_monitorNext)
+                {
+                    _busySelects=0;
+                    _pausing=false;
+                    _monitorNext=now+__MONITOR_PERIOD;
+
+                }
+            }
+            catch (ClosedSelectorException e)
+            {
+                if (isRunning())
+                    LOG.warn(e);
+                else
+                    LOG.ignore(e);
+            }
+            catch (CancelledKeyException e)
+            {
+                LOG.ignore(e);
+            }
+            finally
+            {
+                _selecting=null;
+            }
+        }
+
+
+        /* ------------------------------------------------------------ */
+        private void renewSelector()
+        {
+            try
+            {
+                synchronized (this)
+                {
+                    Selector selector=_selector;
+                    if (selector==null)
+                        return;
+                    final Selector new_selector = Selector.open();
+                    for (SelectionKey k: selector.keys())
+                    {
+                        if (!k.isValid() || k.interestOps()==0)
+                            continue;
+
+                        final SelectableChannel channel = k.channel();
+                        final Object attachment = k.attachment();
+
+                        if (attachment==null)
+                            addChange(channel);
+                        else
+                            addChange(channel,attachment);
+                    }
+                    _selector.close();
+                    _selector=new_selector;
+                }
+            }
+            catch(IOException e)
+            {
+                throw new RuntimeException("recreating selector",e);
+            }
+        }
+
+        /* ------------------------------------------------------------ */
+        public SelectorManager getManager()
+        {
+            return SelectorManager.this;
+        }
+
+        /* ------------------------------------------------------------ */
+        public long getNow()
+        {
+            return _timeout.getNow();
+        }
+
+        /* ------------------------------------------------------------ */
+        /**
+         * @param task The task to timeout. If it implements Runnable, then
+         * expired will be called from a dispatched thread.
+         *
+         * @param timeoutMs
+         */
+        public void scheduleTimeout(Timeout.Task task, long timeoutMs)
+        {
+            if (!(task instanceof Runnable))
+                throw new IllegalArgumentException("!Runnable");
+            _timeout.schedule(task, timeoutMs);
+        }
+
+        /* ------------------------------------------------------------ */
+        public void cancelTimeout(Timeout.Task task)
+        {
+            task.cancel();
+        }
+
+        /* ------------------------------------------------------------ */
+        public void wakeup()
+        {
+            try
+            {
+                Selector selector = _selector;
+                if (selector!=null)
+                    selector.wakeup();
+            }
+            catch(Exception e)
+            {
+                addChange(new ChangeTask()
+                {
+                    public void run()
+                    {
+                        renewSelector();
+                    }
+                });
+
+                renewSelector();
+            }
+        }
+
+        /* ------------------------------------------------------------ */
+        private SelectChannelEndPoint createEndPoint(SocketChannel channel, SelectionKey sKey) throws IOException
+        {
+            SelectChannelEndPoint endp = newEndPoint(channel,this,sKey);
+            LOG.debug("created {}",endp);
+            endPointOpened(endp);
+            _endPoints.put(endp,this);
+            return endp;
+        }
+
+        /* ------------------------------------------------------------ */
+        public void destroyEndPoint(SelectChannelEndPoint endp)
+        {
+            LOG.debug("destroyEndPoint {}",endp);
+            _endPoints.remove(endp);
+            endPointClosed(endp);
+        }
+
+        /* ------------------------------------------------------------ */
+        Selector getSelector()
+        {
+            return _selector;
+        }
+
+        /* ------------------------------------------------------------ */
+        void stop() throws Exception
+        {
+            // Spin for a while waiting for selector to complete
+            // to avoid unneccessary closed channel exceptions
+            try
+            {
+                for (int i=0;i<100 && _selecting!=null;i++)
+                {
+                    wakeup();
+                    Thread.sleep(10);
+                }
+            }
+            catch(Exception e)
+            {
+                LOG.ignore(e);
+            }
+
+            // close endpoints and selector
+            synchronized (this)
+            {
+                Selector selector=_selector;
+                for (SelectionKey key:selector.keys())
+                {
+                    if (key==null)
+                        continue;
+                    Object att=key.attachment();
+                    if (att instanceof EndPoint)
+                    {
+                        EndPoint endpoint = (EndPoint)att;
+                        try
+                        {
+                            endpoint.close();
+                        }
+                        catch(IOException e)
+                        {
+                            LOG.ignore(e);
+                        }
+                    }
+                }
+
+
+                _timeout.cancelAll();
+                try
+                {
+                    selector=_selector;
+                    if (selector != null)
+                        selector.close();
+                }
+                catch (IOException e)
+                {
+                    LOG.ignore(e);
+                }
+                _selector=null;
+            }
+        }
+
+        /* ------------------------------------------------------------ */
+        public String dump()
+        {
+            return AggregateLifeCycle.dump(this);
+        }
+
+        /* ------------------------------------------------------------ */
+        public void dump(Appendable out, String indent) throws IOException
+        {
+            out.append(String.valueOf(this)).append(" id=").append(String.valueOf(_setID)).append("\n");
+
+            Thread selecting = _selecting;
+
+            Object where = "not selecting";
+            StackTraceElement[] trace =selecting==null?null:selecting.getStackTrace();
+            if (trace!=null)
+            {
+                for (StackTraceElement t:trace)
+                    if (t.getClassName().startsWith("org.eclipse.jetty."))
+                    {
+                        where=t;
+                        break;
+                    }
+            }
+
+            Selector selector=_selector;
+            if (selector!=null)
+            {
+                final ArrayList<Object> dump = new ArrayList<Object>(selector.keys().size()*2);
+                dump.add(where);
+
+                final CountDownLatch latch = new CountDownLatch(1);
+
+                addChange(new ChangeTask()
+                {
+                    public void run()
+                    {
+                        dumpKeyState(dump);
+                        latch.countDown();
+                    }
+                });
+
+                try
+                {
+                    latch.await(5,TimeUnit.SECONDS);
+                }
+                catch(InterruptedException e)
+                {
+                    LOG.ignore(e);
+                }
+
+                AggregateLifeCycle.dump(out,indent,dump);
+            }
+        }
+
+        /* ------------------------------------------------------------ */
+        public void dumpKeyState(List<Object> dumpto)
+        {
+            Selector selector=_selector;
+            Set<SelectionKey> keys = selector.keys();
+            dumpto.add(selector + " keys=" + keys.size());
+            for (SelectionKey key: keys)
+            {
+                if (key.isValid())
+                    dumpto.add(key.attachment()+" iOps="+key.interestOps()+" rOps="+key.readyOps());
+                else
+                    dumpto.add(key.attachment()+" iOps=-1 rOps=-1");
+            }
+        }
+
+        /* ------------------------------------------------------------ */
+        public String toString()
+        {
+            Selector selector=_selector;
+            return String.format("%s keys=%d selected=%d",
+                    super.toString(),
+                    selector != null && selector.isOpen() ? selector.keys().size() : -1,
+                    selector != null && selector.isOpen() ? selector.selectedKeys().size() : -1);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    private static class ChannelAndAttachment
+    {
+        final SelectableChannel _channel;
+        final Object _attachment;
+
+        public ChannelAndAttachment(SelectableChannel channel, Object attachment)
+        {
+            super();
+            _channel = channel;
+            _attachment = attachment;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isDeferringInterestedOps0()
+    {
+        return _deferringInterestedOps0;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setDeferringInterestedOps0(boolean deferringInterestedOps0)
+    {
+        _deferringInterestedOps0 = deferringInterestedOps0;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    private interface ChangeTask extends Runnable
+    {}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/io/nio/SslConnection.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,865 @@
+//
+//  ========================================================================
+//  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.io.nio;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.concurrent.atomic.AtomicBoolean;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSession;
+
+import org.eclipse.jetty.io.AbstractConnection;
+import org.eclipse.jetty.io.AsyncEndPoint;
+import org.eclipse.jetty.io.Buffer;
+import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.Timeout.Task;
+
+/* ------------------------------------------------------------ */
+/** SSL Connection.
+ * An AysyncConnection that acts as an interceptor between and EndPoint and another
+ * Connection, that implements TLS encryption using an {@link SSLEngine}.
+ * <p>
+ * The connector uses an {@link AsyncEndPoint} (like {@link SelectChannelEndPoint}) as
+ * it's source/sink of encrypted data.   It then provides {@link #getSslEndPoint()} to
+ * expose a source/sink of unencrypted data to another connection (eg HttpConnection).
+ */
+public class SslConnection extends AbstractConnection implements AsyncConnection
+{
+    private final Logger _logger = Log.getLogger("org.eclipse.jetty.io.nio.ssl");
+
+    private static final NIOBuffer __ZERO_BUFFER=new IndirectNIOBuffer(0);
+
+    private static final ThreadLocal<SslBuffers> __buffers = new ThreadLocal<SslBuffers>();
+    private final SSLEngine _engine;
+    private final SSLSession _session;
+    private AsyncConnection _connection;
+    private final SslEndPoint _sslEndPoint;
+    private int _allocations;
+    private SslBuffers _buffers;
+    private NIOBuffer _inbound;
+    private NIOBuffer _unwrapBuf;
+    private NIOBuffer _outbound;
+    private AsyncEndPoint _aEndp;
+    private boolean _allowRenegotiate=true;
+    private boolean _handshook;
+    private boolean _ishut;
+    private boolean _oshut;
+    private final AtomicBoolean _progressed = new AtomicBoolean();
+
+    /* ------------------------------------------------------------ */
+    /* this is a half baked buffer pool
+     */
+    private static class SslBuffers
+    {
+        final NIOBuffer _in;
+        final NIOBuffer _out;
+        final NIOBuffer _unwrap;
+
+        SslBuffers(int packetSize, int appSize)
+        {
+            _in=new IndirectNIOBuffer(packetSize);
+            _out=new IndirectNIOBuffer(packetSize);
+            _unwrap=new IndirectNIOBuffer(appSize);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public SslConnection(SSLEngine engine,EndPoint endp)
+    {
+        this(engine,endp,System.currentTimeMillis());
+    }
+
+    /* ------------------------------------------------------------ */
+    public SslConnection(SSLEngine engine,EndPoint endp, long timeStamp)
+    {
+        super(endp,timeStamp);
+        _engine=engine;
+        _session=_engine.getSession();
+        _aEndp=(AsyncEndPoint)endp;
+        _sslEndPoint = newSslEndPoint();
+    }
+
+    /* ------------------------------------------------------------ */
+    protected SslEndPoint newSslEndPoint()
+    {
+        return new SslEndPoint();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return True if SSL re-negotiation is allowed (default false)
+     */
+    public boolean isAllowRenegotiate()
+    {
+        return _allowRenegotiate;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set if SSL re-negotiation is allowed. CVE-2009-3555 discovered
+     * a vulnerability in SSL/TLS with re-negotiation.  If your JVM
+     * does not have CVE-2009-3555 fixed, then re-negotiation should
+     * not be allowed.  CVE-2009-3555 was fixed in Sun java 1.6 with a ban
+     * of renegotiates in u19 and with RFC5746 in u22.
+     *
+     * @param allowRenegotiate
+     *            true if re-negotiation is allowed (default false)
+     */
+    public void setAllowRenegotiate(boolean allowRenegotiate)
+    {
+        _allowRenegotiate = allowRenegotiate;
+    }
+
+    /* ------------------------------------------------------------ */
+    private void allocateBuffers()
+    {
+        synchronized (this)
+        {
+            if (_allocations++==0)
+            {
+                if (_buffers==null)
+                {
+                    _buffers=__buffers.get();
+                    if (_buffers==null)
+                        _buffers=new SslBuffers(_session.getPacketBufferSize()*2,_session.getApplicationBufferSize()*2);
+                    _inbound=_buffers._in;
+                    _outbound=_buffers._out;
+                    _unwrapBuf=_buffers._unwrap;
+                    __buffers.set(null);
+                }
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    private void releaseBuffers()
+    {
+        synchronized (this)
+        {
+            if (--_allocations==0)
+            {
+                if (_buffers!=null &&
+                    _inbound.length()==0 &&
+                    _outbound.length()==0 &&
+                    _unwrapBuf.length()==0)
+                {
+                    _inbound=null;
+                    _outbound=null;
+                    _unwrapBuf=null;
+                    __buffers.set(_buffers);
+                    _buffers=null;
+                }
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public Connection handle() throws IOException
+    {
+        try
+        {
+            allocateBuffers();
+
+            boolean progress=true;
+
+            while (progress)
+            {
+                progress=false;
+
+                // If we are handshook let the delegate connection
+                if (_engine.getHandshakeStatus()!=HandshakeStatus.NOT_HANDSHAKING)
+                    progress=process(null,null);
+
+                // handle the delegate connection
+                AsyncConnection next = (AsyncConnection)_connection.handle();
+                if (next!=_connection && next!=null)
+                {
+                    _connection=next;
+                    progress=true;
+                }
+
+                _logger.debug("{} handle {} progress={}", _session, this, progress);
+            }
+        }
+        finally
+        {
+            releaseBuffers();
+
+            if (!_ishut && _sslEndPoint.isInputShutdown() && _sslEndPoint.isOpen())
+            {
+                _ishut=true;
+                try
+                {
+                    _connection.onInputShutdown();
+                }
+                catch(Throwable x)
+                {
+                    _logger.warn("onInputShutdown failed", x);
+                    try{_sslEndPoint.close();}
+                    catch(IOException e2){
+                        _logger.ignore(e2);}
+                }
+            }
+        }
+
+        return this;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isIdle()
+    {
+        return false;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isSuspended()
+    {
+        return false;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void onClose()
+    {
+        Connection connection = _sslEndPoint.getConnection();
+        if (connection != null && connection != this)
+            connection.onClose();
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void onIdleExpired(long idleForMs)
+    {
+        try
+        {
+            _logger.debug("onIdleExpired {}ms on {}",idleForMs,this);
+            if (_endp.isOutputShutdown())
+                _sslEndPoint.close();
+            else
+                _sslEndPoint.shutdownOutput();
+        }
+        catch (IOException e)
+        {
+            _logger.warn(e);
+            super.onIdleExpired(idleForMs);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public void onInputShutdown() throws IOException
+    {
+
+    }
+
+    /* ------------------------------------------------------------ */
+    private synchronized boolean process(Buffer toFill, Buffer toFlush) throws IOException
+    {
+        boolean some_progress=false;
+        try
+        {
+            // We need buffers to progress
+            allocateBuffers();
+
+            // if we don't have a buffer to put received data into
+            if (toFill==null)
+            {
+                // use the unwrapbuffer to hold received data.
+                _unwrapBuf.compact();
+                toFill=_unwrapBuf;
+            }
+            // Else if the fill buffer is too small for the SSL session
+            else if (toFill.capacity()<_session.getApplicationBufferSize())
+            {
+                // fill to the temporary unwrapBuffer
+                boolean progress=process(null,toFlush);
+
+                // if we received any data,
+                if (_unwrapBuf!=null && _unwrapBuf.hasContent())
+                {
+                    // transfer from temp buffer to fill buffer
+                    _unwrapBuf.skip(toFill.put(_unwrapBuf));
+                    return true;
+                }
+                else
+                    // return progress from recursive call
+                    return progress;
+            }
+            // Else if there is some temporary data
+            else if (_unwrapBuf!=null && _unwrapBuf.hasContent())
+            {
+                // transfer from temp buffer to fill buffer
+                _unwrapBuf.skip(toFill.put(_unwrapBuf));
+                return true;
+            }
+
+            // If we are here, we have a buffer ready into which we can put some read data.
+
+            // If we have no data to flush, flush the empty buffer
+            if (toFlush==null)
+                toFlush=__ZERO_BUFFER;
+
+            // While we are making progress processing SSL engine
+            boolean progress=true;
+            while (progress)
+            {
+                progress=false;
+
+                // Do any real IO
+                int filled=0,flushed=0;
+                try
+                {
+                    // Read any available data
+                    if (_inbound.space()>0 && (filled=_endp.fill(_inbound))>0)
+                        progress = true;
+
+                    // flush any output data
+                    if (_outbound.hasContent() && (flushed=_endp.flush(_outbound))>0)
+                        progress = true;
+                }
+                catch (IOException e)
+                {
+                    _endp.close();
+                    throw e;
+                }
+                finally
+                {
+                    _logger.debug("{} {} {} filled={}/{} flushed={}/{}",_session,this,_engine.getHandshakeStatus(),filled,_inbound.length(),flushed,_outbound.length());
+                }
+
+                // handle the current hand share status
+                switch(_engine.getHandshakeStatus())
+                {
+                    case FINISHED:
+                        throw new IllegalStateException();
+
+                    case NOT_HANDSHAKING:
+                    {
+                        // Try unwrapping some application data
+                        if (toFill.space()>0 && _inbound.hasContent() && unwrap(toFill))
+                            progress=true;
+
+                        // Try wrapping some application data
+                        if (toFlush.hasContent() && _outbound.space()>0 && wrap(toFlush))
+                            progress=true;
+                    }
+                    break;
+
+                    case NEED_TASK:
+                    {
+                        // A task needs to be run, so run it!
+                        Runnable task;
+                        while ((task=_engine.getDelegatedTask())!=null)
+                        {
+                            progress=true;
+                            task.run();
+                        }
+
+                    }
+                    break;
+
+                    case NEED_WRAP:
+                    {
+                        // The SSL needs to send some handshake data to the other side
+                        if (_handshook && !_allowRenegotiate)
+                            _endp.close();
+                        else if (wrap(toFlush))
+                            progress=true;
+                    }
+                    break;
+
+                    case NEED_UNWRAP:
+                    {
+                        // The SSL needs to receive some handshake data from the other side
+                        if (_handshook && !_allowRenegotiate)
+                            _endp.close();
+                        else if (!_inbound.hasContent()&&filled==-1)
+                        {
+                            // No more input coming
+                            _endp.shutdownInput();
+                        }
+                        else if (unwrap(toFill))
+                            progress=true;
+                    }
+                    break;
+                }
+
+                // pass on ishut/oshut state
+                if (_endp.isOpen() && _endp.isInputShutdown() && !_inbound.hasContent())
+                    closeInbound();
+
+                if (_endp.isOpen() && _engine.isOutboundDone() && !_outbound.hasContent())
+                    _endp.shutdownOutput();
+
+                // remember if any progress has been made
+                some_progress|=progress;
+            }
+
+            // If we are reading into the temp buffer and it has some content, then we should be dispatched.
+            if (toFill==_unwrapBuf && _unwrapBuf.hasContent() && !_connection.isSuspended())
+                _aEndp.dispatch();
+        }
+        finally
+        {
+            releaseBuffers();
+            if (some_progress)
+                _progressed.set(true);
+        }
+        return some_progress;
+    }
+
+    private void closeInbound()
+    {
+        try
+        {
+            _engine.closeInbound();
+        }
+        catch (SSLException x)
+        {
+            _logger.debug(x);
+        }
+    }
+
+    private synchronized boolean wrap(final Buffer buffer) throws IOException
+    {
+        ByteBuffer bbuf=extractByteBuffer(buffer);
+        final SSLEngineResult result;
+
+        synchronized(bbuf)
+        {
+            _outbound.compact();
+            ByteBuffer out_buffer=_outbound.getByteBuffer();
+            synchronized(out_buffer)
+            {
+                try
+                {
+                    bbuf.position(buffer.getIndex());
+                    bbuf.limit(buffer.putIndex());
+                    out_buffer.position(_outbound.putIndex());
+                    out_buffer.limit(out_buffer.capacity());
+                    result=_engine.wrap(bbuf,out_buffer);
+                    if (_logger.isDebugEnabled())
+                        _logger.debug("{} wrap {} {} consumed={} produced={}",
+                            _session,
+                            result.getStatus(),
+                            result.getHandshakeStatus(),
+                            result.bytesConsumed(),
+                            result.bytesProduced());
+
+
+                    buffer.skip(result.bytesConsumed());
+                    _outbound.setPutIndex(_outbound.putIndex()+result.bytesProduced());
+                }
+                catch(SSLException e)
+                {
+                    _logger.debug(String.valueOf(_endp), e);
+                    _endp.close();
+                    throw e;
+                }
+                finally
+                {
+                    out_buffer.position(0);
+                    out_buffer.limit(out_buffer.capacity());
+                    bbuf.position(0);
+                    bbuf.limit(bbuf.capacity());
+                }
+            }
+        }
+
+        switch(result.getStatus())
+        {
+            case BUFFER_UNDERFLOW:
+                throw new IllegalStateException();
+
+            case BUFFER_OVERFLOW:
+                break;
+
+            case OK:
+                if (result.getHandshakeStatus()==HandshakeStatus.FINISHED)
+                    _handshook=true;
+                break;
+
+            case CLOSED:
+                _logger.debug("wrap CLOSE {} {}",this,result);
+                if (result.getHandshakeStatus()==HandshakeStatus.FINISHED)
+                    _endp.close();
+                break;
+
+            default:
+                _logger.debug("{} wrap default {}",_session,result);
+            throw new IOException(result.toString());
+        }
+
+        return result.bytesConsumed()>0 || result.bytesProduced()>0;
+    }
+
+    private synchronized boolean unwrap(final Buffer buffer) throws IOException
+    {
+        if (!_inbound.hasContent())
+            return false;
+
+        ByteBuffer bbuf=extractByteBuffer(buffer);
+        final SSLEngineResult result;
+
+        synchronized(bbuf)
+        {
+            ByteBuffer in_buffer=_inbound.getByteBuffer();
+            synchronized(in_buffer)
+            {
+                try
+                {
+                    bbuf.position(buffer.putIndex());
+                    bbuf.limit(buffer.capacity());
+                    in_buffer.position(_inbound.getIndex());
+                    in_buffer.limit(_inbound.putIndex());
+
+                    result=_engine.unwrap(in_buffer,bbuf);
+                    if (_logger.isDebugEnabled())
+                        _logger.debug("{} unwrap {} {} consumed={} produced={}",
+                            _session,
+                            result.getStatus(),
+                            result.getHandshakeStatus(),
+                            result.bytesConsumed(),
+                            result.bytesProduced());
+
+                    _inbound.skip(result.bytesConsumed());
+                    _inbound.compact();
+                    buffer.setPutIndex(buffer.putIndex()+result.bytesProduced());
+                }
+                catch(SSLException e)
+                {
+                    _logger.debug(String.valueOf(_endp), e);
+                    _endp.close();
+                    throw e;
+                }
+                finally
+                {
+                    in_buffer.position(0);
+                    in_buffer.limit(in_buffer.capacity());
+                    bbuf.position(0);
+                    bbuf.limit(bbuf.capacity());
+                }
+            }
+        }
+
+        switch(result.getStatus())
+        {
+            case BUFFER_UNDERFLOW:
+                if (_endp.isInputShutdown())
+                    _inbound.clear();
+                break;
+
+            case BUFFER_OVERFLOW:
+                if (_logger.isDebugEnabled()) _logger.debug("{} unwrap {} {}->{}",_session,result.getStatus(),_inbound.toDetailString(),buffer.toDetailString());
+                break;
+
+            case OK:
+                if (result.getHandshakeStatus()==HandshakeStatus.FINISHED)
+                    _handshook=true;
+                break;
+
+            case CLOSED:
+                _logger.debug("unwrap CLOSE {} {}",this,result);
+                if (result.getHandshakeStatus()==HandshakeStatus.FINISHED)
+                    _endp.close();
+                break;
+
+            default:
+                _logger.debug("{} wrap default {}",_session,result);
+            throw new IOException(result.toString());
+        }
+
+        //if (LOG.isDebugEnabled() && result.bytesProduced()>0)
+        //    LOG.debug("{} unwrapped '{}'",_session,buffer);
+
+        return result.bytesConsumed()>0 || result.bytesProduced()>0;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    private ByteBuffer extractByteBuffer(Buffer buffer)
+    {
+        if (buffer.buffer() instanceof NIOBuffer)
+            return ((NIOBuffer)buffer.buffer()).getByteBuffer();
+        return ByteBuffer.wrap(buffer.array());
+    }
+
+    /* ------------------------------------------------------------ */
+    public AsyncEndPoint getSslEndPoint()
+    {
+        return _sslEndPoint;
+    }
+
+    /* ------------------------------------------------------------ */
+    public String toString()
+    {
+        return String.format("%s %s", super.toString(), _sslEndPoint);
+    }
+
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    public class SslEndPoint implements AsyncEndPoint
+    {
+        public SSLEngine getSslEngine()
+        {
+            return _engine;
+        }
+
+        public AsyncEndPoint getEndpoint()
+        {
+            return _aEndp;
+        }
+
+        public void shutdownOutput() throws IOException
+        {
+            synchronized (SslConnection.this)
+            {
+                _logger.debug("{} ssl endp.oshut {}",_session,this);
+                _engine.closeOutbound();
+                _oshut=true;
+            }
+            flush();
+        }
+
+        public boolean isOutputShutdown()
+        {
+            synchronized (SslConnection.this)
+            {
+                return _oshut||!isOpen()||_engine.isOutboundDone();
+            }
+        }
+
+        public void shutdownInput() throws IOException
+        {
+            _logger.debug("{} ssl endp.ishut!",_session);
+            // We do not do a closeInput here, as SSL does not support half close.
+            // isInputShutdown works it out itself from buffer state and underlying endpoint state.
+        }
+
+        public boolean isInputShutdown()
+        {
+            synchronized (SslConnection.this)
+            {
+                return _endp.isInputShutdown() &&
+                !(_unwrapBuf!=null&&_unwrapBuf.hasContent()) &&
+                !(_inbound!=null&&_inbound.hasContent());
+            }
+        }
+
+        public void close() throws IOException
+        {
+            _logger.debug("{} ssl endp.close",_session);
+            _endp.close();
+        }
+
+        public int fill(Buffer buffer) throws IOException
+        {
+            int size=buffer.length();
+            process(buffer, null);
+
+            int filled=buffer.length()-size;
+
+            if (filled==0 && isInputShutdown())
+                return -1;
+            return filled;
+        }
+
+        public int flush(Buffer buffer) throws IOException
+        {
+            int size = buffer.length();
+            process(null, buffer);
+            return size-buffer.length();
+        }
+
+        public int flush(Buffer header, Buffer buffer, Buffer trailer) throws IOException
+        {
+            if (header!=null && header.hasContent())
+                return flush(header);
+            if (buffer!=null && buffer.hasContent())
+                return flush(buffer);
+            if (trailer!=null && trailer.hasContent())
+                return flush(trailer);
+            return 0;
+        }
+
+        public boolean blockReadable(long millisecs) throws IOException
+        {
+            long now = System.currentTimeMillis();
+            long end=millisecs>0?(now+millisecs):Long.MAX_VALUE;
+
+            while (now<end)
+            {
+                if (process(null,null))
+                    break;
+                _endp.blockReadable(end-now);
+                now = System.currentTimeMillis();
+            }
+
+            return now<end;
+        }
+
+        public boolean blockWritable(long millisecs) throws IOException
+        {
+            return _endp.blockWritable(millisecs);
+        }
+
+        public boolean isOpen()
+        {
+            return _endp.isOpen();
+        }
+
+        public Object getTransport()
+        {
+            return _endp;
+        }
+
+        public void flush() throws IOException
+        {
+            process(null, null);
+        }
+
+        public void dispatch()
+        {
+            _aEndp.dispatch();
+        }
+
+        public void asyncDispatch()
+        {
+            _aEndp.asyncDispatch();
+        }
+
+        public void scheduleWrite()
+        {
+            _aEndp.scheduleWrite();
+        }
+
+        public void onIdleExpired(long idleForMs)
+        {
+            _aEndp.onIdleExpired(idleForMs);
+        }
+
+        public void setCheckForIdle(boolean check)
+        {
+            _aEndp.setCheckForIdle(check);
+        }
+
+        public boolean isCheckForIdle()
+        {
+            return _aEndp.isCheckForIdle();
+        }
+
+        public void scheduleTimeout(Task task, long timeoutMs)
+        {
+            _aEndp.scheduleTimeout(task,timeoutMs);
+        }
+
+        public void cancelTimeout(Task task)
+        {
+            _aEndp.cancelTimeout(task);
+        }
+
+        public boolean isWritable()
+        {
+            return _aEndp.isWritable();
+        }
+
+        public boolean hasProgressed()
+        {
+            return _progressed.getAndSet(false);
+        }
+
+        public String getLocalAddr()
+        {
+            return _aEndp.getLocalAddr();
+        }
+
+        public String getLocalHost()
+        {
+            return _aEndp.getLocalHost();
+        }
+
+        public int getLocalPort()
+        {
+            return _aEndp.getLocalPort();
+        }
+
+        public String getRemoteAddr()
+        {
+            return _aEndp.getRemoteAddr();
+        }
+
+        public String getRemoteHost()
+        {
+            return _aEndp.getRemoteHost();
+        }
+
+        public int getRemotePort()
+        {
+            return _aEndp.getRemotePort();
+        }
+
+        public boolean isBlocking()
+        {
+            return false;
+        }
+
+        public int getMaxIdleTime()
+        {
+            return _aEndp.getMaxIdleTime();
+        }
+
+        public void setMaxIdleTime(int timeMs) throws IOException
+        {
+            _aEndp.setMaxIdleTime(timeMs);
+        }
+
+        public Connection getConnection()
+        {
+            return _connection;
+        }
+
+        public void setConnection(Connection connection)
+        {
+            _connection=(AsyncConnection)connection;
+        }
+
+        public String toString()
+        {
+            // Do NOT use synchronized (SslConnection.this)
+            // because it's very easy to deadlock when debugging is enabled.
+            // We do a best effort to print the right toString() and that's it.
+            Buffer inbound = _inbound;
+            Buffer outbound = _outbound;
+            Buffer unwrap = _unwrapBuf;
+            int i = inbound == null? -1 : inbound.length();
+            int o = outbound == null ? -1 : outbound.length();
+            int u = unwrap == null ? -1 : unwrap.length();
+            return String.format("SSL %s i/o/u=%d/%d/%d ishut=%b oshut=%b {%s}",
+                    _engine.getHandshakeStatus(),
+                    i, o, u,
+                    _ishut, _oshut,
+                    _connection);
+        }
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/AbstractConnector.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,1222 @@
+//
+//  ========================================================================
+//  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.server;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.util.concurrent.atomic.AtomicLong;
+
+import javax.servlet.ServletRequest;
+
+import org.eclipse.jetty.http.HttpBuffers;
+import org.eclipse.jetty.http.HttpBuffersImpl;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpHeaders;
+import org.eclipse.jetty.http.HttpSchemes;
+import org.eclipse.jetty.io.Buffers;
+import org.eclipse.jetty.io.Buffers.Type;
+import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.EofException;
+import org.eclipse.jetty.util.component.AggregateLifeCycle;
+import org.eclipse.jetty.util.component.Dumpable;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.statistic.CounterStatistic;
+import org.eclipse.jetty.util.statistic.SampleStatistic;
+import org.eclipse.jetty.util.thread.ThreadPool;
+
+/**
+ * Abstract Connector implementation. This abstract implementation of the Connector interface provides:
+ * <ul>
+ * <li>AbstractLifeCycle implementation</li>
+ * <li>Implementations for connector getters and setters</li>
+ * <li>Buffer management</li>
+ * <li>Socket configuration</li>
+ * <li>Base acceptor thread</li>
+ * <li>Optional reverse proxy headers checking</li>
+ * </ul>
+ */
+public abstract class AbstractConnector extends AggregateLifeCycle implements HttpBuffers, Connector, Dumpable
+{
+    private static final Logger LOG = Log.getLogger(AbstractConnector.class);
+
+    private String _name;
+
+    private Server _server;
+    private ThreadPool _threadPool;
+    private String _host;
+    private int _port = 0;
+    private String _integralScheme = HttpSchemes.HTTPS;
+    private int _integralPort = 0;
+    private String _confidentialScheme = HttpSchemes.HTTPS;
+    private int _confidentialPort = 0;
+    private int _acceptQueueSize = 0;
+    private int _acceptors = 1;
+    private int _acceptorPriorityOffset = 0;
+    private boolean _useDNS;
+    private boolean _forwarded;
+    private String _hostHeader;
+
+    private String _forwardedHostHeader = HttpHeaders.X_FORWARDED_HOST;
+    private String _forwardedServerHeader = HttpHeaders.X_FORWARDED_SERVER;
+    private String _forwardedForHeader = HttpHeaders.X_FORWARDED_FOR;
+    private String _forwardedProtoHeader = HttpHeaders.X_FORWARDED_PROTO;
+    private String _forwardedCipherSuiteHeader;
+    private String _forwardedSslSessionIdHeader;
+    private boolean _reuseAddress = true;
+
+    protected int _maxIdleTime = 200000;
+    protected int _lowResourceMaxIdleTime = -1;
+    protected int _soLingerTime = -1;
+
+    private transient Thread[] _acceptorThreads;
+
+    private final AtomicLong _statsStartedAt = new AtomicLong(-1L);
+
+    /** connections to server */
+    private final CounterStatistic _connectionStats = new CounterStatistic();
+    /** requests per connection */
+    private final SampleStatistic _requestStats = new SampleStatistic();
+    /** duration of a connection */
+    private final SampleStatistic _connectionDurationStats = new SampleStatistic();
+
+    protected final HttpBuffersImpl _buffers = new HttpBuffersImpl();
+
+    /* ------------------------------------------------------------ */
+    /**
+     */
+    public AbstractConnector()
+    {
+        addBean(_buffers);
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     */
+    public Server getServer()
+    {
+        return _server;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setServer(Server server)
+    {
+        _server = server;
+    }
+
+    /* ------------------------------------------------------------ */
+    public ThreadPool getThreadPool()
+    {
+        return _threadPool;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set the ThreadPool.
+     * The threadpool passed is added via {@link #addBean(Object)} so that 
+     * it's lifecycle may be managed as a {@link AggregateLifeCycle}.
+     * @param pool the threadPool to set
+     */
+    public void setThreadPool(ThreadPool pool)
+    {
+        removeBean(_threadPool);
+        _threadPool = pool;
+        addBean(_threadPool);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     */
+    public void setHost(String host)
+    {
+        _host = host;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     */
+    public String getHost()
+    {
+        return _host;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setPort(int port)
+    {
+        _port = port;
+    }
+
+    /* ------------------------------------------------------------ */
+    public int getPort()
+    {
+        return _port;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the maxIdleTime.
+     */
+    public int getMaxIdleTime()
+    {
+        return _maxIdleTime;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set the maximum Idle time for a connection, which roughly translates to the {@link Socket#setSoTimeout(int)} call, although with NIO implementations
+     * other mechanisms may be used to implement the timeout. The max idle time is applied:
+     * <ul>
+     * <li>When waiting for a new request to be received on a connection</li>
+     * <li>When reading the headers and content of a request</li>
+     * <li>When writing the headers and content of a response</li>
+     * </ul>
+     * Jetty interprets this value as the maximum time between some progress being made on the connection. So if a single byte is read or written, then the
+     * timeout (if implemented by jetty) is reset. However, in many instances, the reading/writing is delegated to the JVM, and the semantic is more strictly
+     * enforced as the maximum time a single read/write operation can take. Note, that as Jetty supports writes of memory mapped file buffers, then a write may
+     * take many 10s of seconds for large content written to a slow device.
+     * <p>
+     * Previously, Jetty supported separate idle timeouts and IO operation timeouts, however the expense of changing the value of soTimeout was significant, so
+     * these timeouts were merged. With the advent of NIO, it may be possible to again differentiate these values (if there is demand).
+     *
+     * @param maxIdleTime
+     *            The maxIdleTime to set.
+     */
+    public void setMaxIdleTime(int maxIdleTime)
+    {
+        _maxIdleTime = maxIdleTime;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the maxIdleTime when resources are low.
+     */
+    public int getLowResourcesMaxIdleTime()
+    {
+        return _lowResourceMaxIdleTime;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param maxIdleTime
+     *            The maxIdleTime to set when resources are low.
+     */
+    public void setLowResourcesMaxIdleTime(int maxIdleTime)
+    {
+        _lowResourceMaxIdleTime = maxIdleTime;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the maxIdleTime when resources are low.
+     * @deprecated
+     */
+    @Deprecated
+    public final int getLowResourceMaxIdleTime()
+    {
+        return getLowResourcesMaxIdleTime();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param maxIdleTime
+     *            The maxIdleTime to set when resources are low.
+     * @deprecated
+     */
+    @Deprecated
+    public final void setLowResourceMaxIdleTime(int maxIdleTime)
+    {
+        setLowResourcesMaxIdleTime(maxIdleTime);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the soLingerTime.
+     */
+    public int getSoLingerTime()
+    {
+        return _soLingerTime;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the acceptQueueSize.
+     */
+    public int getAcceptQueueSize()
+    {
+        return _acceptQueueSize;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param acceptQueueSize
+     *            The acceptQueueSize to set.
+     */
+    public void setAcceptQueueSize(int acceptQueueSize)
+    {
+        _acceptQueueSize = acceptQueueSize;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the number of acceptor threads.
+     */
+    public int getAcceptors()
+    {
+        return _acceptors;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param acceptors
+     *            The number of acceptor threads to set.
+     */
+    public void setAcceptors(int acceptors)
+    {
+        if (acceptors > 2 * Runtime.getRuntime().availableProcessors())
+            LOG.warn("Acceptors should be <=2*availableProcessors: " + this);
+        _acceptors = acceptors;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param soLingerTime
+     *            The soLingerTime to set or -1 to disable.
+     */
+    public void setSoLingerTime(int soLingerTime)
+    {
+        _soLingerTime = soLingerTime;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    protected void doStart() throws Exception
+    {
+        if (_server == null)
+            throw new IllegalStateException("No server");
+
+        // open listener port
+        open();
+
+        if (_threadPool == null)
+        {
+            _threadPool = _server.getThreadPool();
+            addBean(_threadPool,false);
+        }
+
+        super.doStart();
+
+        // Start selector thread
+        synchronized (this)
+        {
+            _acceptorThreads = new Thread[getAcceptors()];
+
+            for (int i = 0; i < _acceptorThreads.length; i++)
+                if (!_threadPool.dispatch(new Acceptor(i)))
+                    throw new IllegalStateException("!accepting");
+            if (_threadPool.isLowOnThreads())
+                LOG.warn("insufficient threads configured for {}",this);
+        }
+
+        LOG.info("Started {}",this);
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    protected void doStop() throws Exception
+    {
+        try
+        {
+            close();
+        }
+        catch (IOException e)
+        {
+            LOG.warn(e);
+        }
+
+        super.doStop();
+
+        Thread[] acceptors;
+        synchronized (this)
+        {
+            acceptors = _acceptorThreads;
+            _acceptorThreads = null;
+        }
+        if (acceptors != null)
+        {
+            for (Thread thread : acceptors)
+            {
+                if (thread != null)
+                    thread.interrupt();
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public void join() throws InterruptedException
+    {
+        Thread[] threads;
+        synchronized(this)
+        {
+            threads=_acceptorThreads;
+        }
+        if (threads != null)
+            for (Thread thread : threads)
+                if (thread != null)
+                    thread.join();
+    }
+
+    /* ------------------------------------------------------------ */
+    protected void configure(Socket socket) throws IOException
+    {
+        try
+        {
+            socket.setTcpNoDelay(true);
+            if (_soLingerTime >= 0)
+                socket.setSoLinger(true,_soLingerTime / 1000);
+            else
+                socket.setSoLinger(false,0);
+        }
+        catch (Exception e)
+        {
+            LOG.ignore(e);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public void customize(EndPoint endpoint, Request request) throws IOException
+    {
+        if (isForwarded())
+            checkForwardedHeaders(endpoint,request);
+    }
+
+    /* ------------------------------------------------------------ */
+    protected void checkForwardedHeaders(EndPoint endpoint, Request request) throws IOException
+    {
+        HttpFields httpFields = request.getConnection().getRequestFields();
+
+        // Do SSL first
+        if (getForwardedCipherSuiteHeader()!=null)
+        {
+            String cipher_suite=httpFields.getStringField(getForwardedCipherSuiteHeader());
+            if (cipher_suite!=null)
+                request.setAttribute("javax.servlet.request.cipher_suite",cipher_suite);
+        }
+        if (getForwardedSslSessionIdHeader()!=null)
+        {
+            String ssl_session_id=httpFields.getStringField(getForwardedSslSessionIdHeader());
+            if(ssl_session_id!=null)
+            {
+                request.setAttribute("javax.servlet.request.ssl_session_id", ssl_session_id);
+                request.setScheme(HttpSchemes.HTTPS);
+            }
+        }
+
+        // Retrieving headers from the request
+        String forwardedHost = getLeftMostFieldValue(httpFields,getForwardedHostHeader());
+        String forwardedServer = getLeftMostFieldValue(httpFields,getForwardedServerHeader());
+        String forwardedFor = getLeftMostFieldValue(httpFields,getForwardedForHeader());
+        String forwardedProto = getLeftMostFieldValue(httpFields,getForwardedProtoHeader());
+
+        if (_hostHeader != null)
+        {
+            // Update host header
+            httpFields.put(HttpHeaders.HOST_BUFFER,_hostHeader);
+            request.setServerName(null);
+            request.setServerPort(-1);
+            request.getServerName();
+        }
+        else if (forwardedHost != null)
+        {
+            // Update host header
+            httpFields.put(HttpHeaders.HOST_BUFFER,forwardedHost);
+            request.setServerName(null);
+            request.setServerPort(-1);
+            request.getServerName();
+        }
+        else if (forwardedServer != null)
+        {
+            // Use provided server name
+            request.setServerName(forwardedServer);
+        }
+
+        if (forwardedFor != null)
+        {
+            request.setRemoteAddr(forwardedFor);
+            InetAddress inetAddress = null;
+
+            if (_useDNS)
+            {
+                try
+                {
+                    inetAddress = InetAddress.getByName(forwardedFor);
+                }
+                catch (UnknownHostException e)
+                {
+                    LOG.ignore(e);
+                }
+            }
+
+            request.setRemoteHost(inetAddress == null?forwardedFor:inetAddress.getHostName());
+        }
+
+        if (forwardedProto != null)
+        {
+            request.setScheme(forwardedProto);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    protected String getLeftMostFieldValue(HttpFields fields, String header)
+    {
+        if (header == null)
+            return null;
+
+        String headerValue = fields.getStringField(header);
+
+        if (headerValue == null)
+            return null;
+
+        int commaIndex = headerValue.indexOf(',');
+
+        if (commaIndex == -1)
+        {
+            // Single value
+            return headerValue;
+        }
+
+        // The left-most value is the farthest downstream client
+        return headerValue.substring(0,commaIndex);
+    }
+
+    /* ------------------------------------------------------------ */
+    public void persist(EndPoint endpoint) throws IOException
+    {
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.jetty.server.Connector#getConfidentialPort()
+     */
+    public int getConfidentialPort()
+    {
+        return _confidentialPort;
+    }
+
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.jetty.server.Connector#getConfidentialScheme()
+     */
+    public String getConfidentialScheme()
+    {
+        return _confidentialScheme;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.jetty.server.Connector#isConfidential(org.eclipse.jetty.server .Request)
+     */
+    public boolean isIntegral(Request request)
+    {
+        return false;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.jetty.server.Connector#getConfidentialPort()
+     */
+    public int getIntegralPort()
+    {
+        return _integralPort;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.jetty.server.Connector#getIntegralScheme()
+     */
+    public String getIntegralScheme()
+    {
+        return _integralScheme;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.jetty.server.Connector#isConfidential(org.eclipse.jetty.server.Request)
+     */
+    public boolean isConfidential(Request request)
+    {
+        return _forwarded && request.getScheme().equalsIgnoreCase(HttpSchemes.HTTPS);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param confidentialPort
+     *            The confidentialPort to set.
+     */
+    public void setConfidentialPort(int confidentialPort)
+    {
+        _confidentialPort = confidentialPort;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param confidentialScheme
+     *            The confidentialScheme to set.
+     */
+    public void setConfidentialScheme(String confidentialScheme)
+    {
+        _confidentialScheme = confidentialScheme;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param integralPort
+     *            The integralPort to set.
+     */
+    public void setIntegralPort(int integralPort)
+    {
+        _integralPort = integralPort;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param integralScheme
+     *            The integralScheme to set.
+     */
+    public void setIntegralScheme(String integralScheme)
+    {
+        _integralScheme = integralScheme;
+    }
+
+    /* ------------------------------------------------------------ */
+    protected abstract void accept(int acceptorID) throws IOException, InterruptedException;
+
+    /* ------------------------------------------------------------ */
+    public void stopAccept(int acceptorID) throws Exception
+    {
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean getResolveNames()
+    {
+        return _useDNS;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setResolveNames(boolean resolve)
+    {
+        _useDNS = resolve;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Is reverse proxy handling on?
+     *
+     * @return true if this connector is checking the x-forwarded-for/host/server headers
+     */
+    public boolean isForwarded()
+    {
+        return _forwarded;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set reverse proxy handling. If set to true, then the X-Forwarded headers (or the headers set in their place) are looked for to set the request protocol,
+     * host, server and client ip.
+     *
+     * @param check
+     *            true if this connector is checking the x-forwarded-for/host/server headers
+     * @see #setForwardedForHeader(String)
+     * @see #setForwardedHostHeader(String)
+     * @see #setForwardedProtoHeader(String)
+     * @see #setForwardedServerHeader(String)
+     */
+    public void setForwarded(boolean check)
+    {
+        if (check)
+            LOG.debug("{} is forwarded",this);
+        _forwarded = check;
+    }
+
+    /* ------------------------------------------------------------ */
+    public String getHostHeader()
+    {
+        return _hostHeader;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set a forced valued for the host header to control what is returned by {@link ServletRequest#getServerName()} and {@link ServletRequest#getServerPort()}.
+     * This value is only used if {@link #isForwarded()} is true.
+     *
+     * @param hostHeader
+     *            The value of the host header to force.
+     */
+    public void setHostHeader(String hostHeader)
+    {
+        _hostHeader = hostHeader;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     *
+     * @see #setForwarded(boolean)
+     */
+    public String getForwardedHostHeader()
+    {
+        return _forwardedHostHeader;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param forwardedHostHeader
+     *            The header name for forwarded hosts (default x-forwarded-host)
+     * @see #setForwarded(boolean)
+     */
+    public void setForwardedHostHeader(String forwardedHostHeader)
+    {
+        _forwardedHostHeader = forwardedHostHeader;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return the header name for forwarded server.
+     * @see #setForwarded(boolean)
+     */
+    public String getForwardedServerHeader()
+    {
+        return _forwardedServerHeader;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param forwardedServerHeader
+     *            The header name for forwarded server (default x-forwarded-server)
+     * @see #setForwarded(boolean)
+     */
+    public void setForwardedServerHeader(String forwardedServerHeader)
+    {
+        _forwardedServerHeader = forwardedServerHeader;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see #setForwarded(boolean)
+     */
+    public String getForwardedForHeader()
+    {
+        return _forwardedForHeader;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param forwardedRemoteAddressHeader
+     *            The header name for forwarded for (default x-forwarded-for)
+     * @see #setForwarded(boolean)
+     */
+    public void setForwardedForHeader(String forwardedRemoteAddressHeader)
+    {
+        _forwardedForHeader = forwardedRemoteAddressHeader;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Get the forwardedProtoHeader.
+     *
+     * @return the forwardedProtoHeader (default X-Forwarded-For)
+     * @see #setForwarded(boolean)
+     */
+    public String getForwardedProtoHeader()
+    {
+        return _forwardedProtoHeader;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set the forwardedProtoHeader.
+     *
+     * @param forwardedProtoHeader
+     *            the forwardedProtoHeader to set (default X-Forwarded-For)
+     * @see #setForwarded(boolean)
+     */
+    public void setForwardedProtoHeader(String forwardedProtoHeader)
+    {
+        _forwardedProtoHeader = forwardedProtoHeader;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The header name holding a forwarded cipher suite (default null)
+     */
+    public String getForwardedCipherSuiteHeader()
+    {
+        return _forwardedCipherSuiteHeader;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param forwardedCipherSuite
+     *            The header name holding a forwarded cipher suite (default null)
+     */
+    public void setForwardedCipherSuiteHeader(String forwardedCipherSuite)
+    {
+        _forwardedCipherSuiteHeader = forwardedCipherSuite;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The header name holding a forwarded SSL Session ID (default null)
+     */
+    public String getForwardedSslSessionIdHeader()
+    {
+        return _forwardedSslSessionIdHeader;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param forwardedSslSessionId
+     *            The header name holding a forwarded SSL Session ID (default null)
+     */
+    public void setForwardedSslSessionIdHeader(String forwardedSslSessionId)
+    {
+        _forwardedSslSessionIdHeader = forwardedSslSessionId;
+    }
+
+    public int getRequestBufferSize()
+    {
+        return _buffers.getRequestBufferSize();
+    }
+
+    public void setRequestBufferSize(int requestBufferSize)
+    {
+        _buffers.setRequestBufferSize(requestBufferSize);
+    }
+
+    public int getRequestHeaderSize()
+    {
+        return _buffers.getRequestHeaderSize();
+    }
+
+    public void setRequestHeaderSize(int requestHeaderSize)
+    {
+        _buffers.setRequestHeaderSize(requestHeaderSize);
+    }
+
+    public int getResponseBufferSize()
+    {
+        return _buffers.getResponseBufferSize();
+    }
+
+    public void setResponseBufferSize(int responseBufferSize)
+    {
+        _buffers.setResponseBufferSize(responseBufferSize);
+    }
+
+    public int getResponseHeaderSize()
+    {
+        return _buffers.getResponseHeaderSize();
+    }
+
+    public void setResponseHeaderSize(int responseHeaderSize)
+    {
+        _buffers.setResponseHeaderSize(responseHeaderSize);
+    }
+
+    public Type getRequestBufferType()
+    {
+        return _buffers.getRequestBufferType();
+    }
+
+    public Type getRequestHeaderType()
+    {
+        return _buffers.getRequestHeaderType();
+    }
+
+    public Type getResponseBufferType()
+    {
+        return _buffers.getResponseBufferType();
+    }
+
+    public Type getResponseHeaderType()
+    {
+        return _buffers.getResponseHeaderType();
+    }
+
+    public void setRequestBuffers(Buffers requestBuffers)
+    {
+        _buffers.setRequestBuffers(requestBuffers);
+    }
+
+    public void setResponseBuffers(Buffers responseBuffers)
+    {
+        _buffers.setResponseBuffers(responseBuffers);
+    }
+
+    public Buffers getRequestBuffers()
+    {
+        return _buffers.getRequestBuffers();
+    }
+
+    public Buffers getResponseBuffers()
+    {
+        return _buffers.getResponseBuffers();
+    }
+
+    public void setMaxBuffers(int maxBuffers)
+    {
+        _buffers.setMaxBuffers(maxBuffers);
+    }
+
+    public int getMaxBuffers()
+    {
+        return _buffers.getMaxBuffers();
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public String toString()
+    {
+        return String.format("%s@%s:%d",
+                getClass().getSimpleName(),
+                getHost()==null?"0.0.0.0":getHost(),
+                getLocalPort()<=0?getPort():getLocalPort());
+    }
+
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    private class Acceptor implements Runnable
+    {
+        int _acceptor = 0;
+
+        Acceptor(int id)
+        {
+            _acceptor = id;
+        }
+
+        /* ------------------------------------------------------------ */
+        public void run()
+        {
+            Thread current = Thread.currentThread();
+            String name;
+            synchronized (AbstractConnector.this)
+            {
+                if (_acceptorThreads == null)
+                    return;
+
+                _acceptorThreads[_acceptor] = current;
+                name = _acceptorThreads[_acceptor].getName();
+                current.setName(name + " Acceptor" + _acceptor + " " + AbstractConnector.this);
+            }
+            int old_priority = current.getPriority();
+
+            try
+            {
+                current.setPriority(old_priority - _acceptorPriorityOffset);
+                while (isRunning() && getConnection() != null)
+                {
+                    try
+                    {
+                        accept(_acceptor);
+                    }
+                    catch (EofException e)
+                    {
+                        LOG.ignore(e);
+                    }
+                    catch (IOException e)
+                    {
+                        LOG.ignore(e);
+                    }
+                    catch (InterruptedException x)
+                    {
+                        // Connector has been stopped
+                        LOG.ignore(x);
+                    }
+                    catch (Throwable e)
+                    {
+                        LOG.warn(e);
+                    }
+                }
+            }
+            finally
+            {
+                current.setPriority(old_priority);
+                current.setName(name);
+
+                synchronized (AbstractConnector.this)
+                {
+                    if (_acceptorThreads != null)
+                        _acceptorThreads[_acceptor] = null;
+                }
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public String getName()
+    {
+        if (_name == null)
+            _name = (getHost() == null?"0.0.0.0":getHost()) + ":" + (getLocalPort() <= 0?getPort():getLocalPort());
+        return _name;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setName(String name)
+    {
+        _name = name;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Get the number of requests handled by this connector since last call of statsReset(). If setStatsOn(false) then this is undefined.
+     */
+    public int getRequests()
+    {
+        return (int)_requestStats.getTotal();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the connectionsDurationTotal.
+     */
+    public long getConnectionsDurationTotal()
+    {
+        return _connectionDurationStats.getTotal();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Number of connections accepted by the server since statsReset() called. Undefined if setStatsOn(false).
+     */
+    public int getConnections()
+    {
+        return (int)_connectionStats.getTotal();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Number of connections currently open that were opened since statsReset() called. Undefined if setStatsOn(false).
+     */
+    public int getConnectionsOpen()
+    {
+        return (int)_connectionStats.getCurrent();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Maximum number of connections opened simultaneously since statsReset() called. Undefined if setStatsOn(false).
+     */
+    public int getConnectionsOpenMax()
+    {
+        return (int)_connectionStats.getMax();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Mean duration in milliseconds of open connections since statsReset() called. Undefined if setStatsOn(false).
+     */
+    public double getConnectionsDurationMean()
+    {
+        return _connectionDurationStats.getMean();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Maximum duration in milliseconds of an open connection since statsReset() called. Undefined if setStatsOn(false).
+     */
+    public long getConnectionsDurationMax()
+    {
+        return _connectionDurationStats.getMax();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Standard deviation of duration in milliseconds of open connections since statsReset() called. Undefined if setStatsOn(false).
+     */
+    public double getConnectionsDurationStdDev()
+    {
+        return _connectionDurationStats.getStdDev();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Mean number of requests per connection since statsReset() called. Undefined if setStatsOn(false).
+     */
+    public double getConnectionsRequestsMean()
+    {
+        return _requestStats.getMean();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Maximum number of requests per connection since statsReset() called. Undefined if setStatsOn(false).
+     */
+    public int getConnectionsRequestsMax()
+    {
+        return (int)_requestStats.getMax();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Standard deviation of number of requests per connection since statsReset() called. Undefined if setStatsOn(false).
+     */
+    public double getConnectionsRequestsStdDev()
+    {
+        return _requestStats.getStdDev();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Reset statistics.
+     */
+    public void statsReset()
+    {
+        updateNotEqual(_statsStartedAt,-1,System.currentTimeMillis());
+
+        _requestStats.reset();
+        _connectionStats.reset();
+        _connectionDurationStats.reset();
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setStatsOn(boolean on)
+    {
+        if (on && _statsStartedAt.get() != -1)
+            return;
+
+        if (LOG.isDebugEnabled())
+            LOG.debug("Statistics on = " + on + " for " + this);
+
+        statsReset();
+        _statsStartedAt.set(on?System.currentTimeMillis():-1);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return True if statistics collection is turned on.
+     */
+    public boolean getStatsOn()
+    {
+        return _statsStartedAt.get() != -1;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Timestamp stats were started at.
+     */
+    public long getStatsOnMs()
+    {
+        long start = _statsStartedAt.get();
+
+        return (start != -1)?(System.currentTimeMillis() - start):0;
+    }
+
+    /* ------------------------------------------------------------ */
+    protected void connectionOpened(Connection connection)
+    {
+        if (_statsStartedAt.get() == -1)
+            return;
+
+        _connectionStats.increment();
+    }
+
+    /* ------------------------------------------------------------ */
+    protected void connectionUpgraded(Connection oldConnection, Connection newConnection)
+    {
+        _requestStats.set((oldConnection instanceof AbstractHttpConnection)?((AbstractHttpConnection)oldConnection).getRequests():0);
+    }
+
+    /* ------------------------------------------------------------ */
+    protected void connectionClosed(Connection connection)
+    {
+        connection.onClose();
+
+        if (_statsStartedAt.get() == -1)
+            return;
+
+        long duration = System.currentTimeMillis() - connection.getTimeStamp();
+        int requests = (connection instanceof AbstractHttpConnection)?((AbstractHttpConnection)connection).getRequests():0;
+        _requestStats.set(requests);
+        _connectionStats.decrement();
+        _connectionDurationStats.set(duration);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return the acceptorPriority
+     */
+    public int getAcceptorPriorityOffset()
+    {
+        return _acceptorPriorityOffset;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set the priority offset of the acceptor threads. The priority is adjusted by this amount (default 0) to either favour the acceptance of new threads and
+     * newly active connections or to favour the handling of already dispatched connections.
+     *
+     * @param offset
+     *            the amount to alter the priority of the acceptor threads.
+     */
+    public void setAcceptorPriorityOffset(int offset)
+    {
+        _acceptorPriorityOffset = offset;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return True if the the server socket will be opened in SO_REUSEADDR mode.
+     */
+    public boolean getReuseAddress()
+    {
+        return _reuseAddress;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param reuseAddress
+     *            True if the the server socket will be opened in SO_REUSEADDR mode.
+     */
+    public void setReuseAddress(boolean reuseAddress)
+    {
+        _reuseAddress = reuseAddress;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isLowResources()
+    {
+        if (_threadPool != null)
+            return _threadPool.isLowOnThreads();
+        return _server.getThreadPool().isLowOnThreads();
+    }
+
+    /* ------------------------------------------------------------ */
+    private void updateNotEqual(AtomicLong valueHolder, long compare, long value)
+    {
+        long oldValue = valueHolder.get();
+        while (compare != oldValue)
+        {
+            if (valueHolder.compareAndSet(oldValue,value))
+                break;
+            oldValue = valueHolder.get();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/AbstractHttpConnection.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,1273 @@
+//
+//  ========================================================================
+//  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.server;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+
+import javax.servlet.DispatcherType;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.continuation.ContinuationThrowable;
+import org.eclipse.jetty.http.EncodedHttpURI;
+import org.eclipse.jetty.http.Generator;
+import org.eclipse.jetty.http.HttpBuffers;
+import org.eclipse.jetty.http.HttpContent;
+import org.eclipse.jetty.http.HttpException;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpGenerator;
+import org.eclipse.jetty.http.HttpHeaderValues;
+import org.eclipse.jetty.http.HttpHeaders;
+import org.eclipse.jetty.http.HttpMethods;
+import org.eclipse.jetty.http.HttpParser;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.HttpURI;
+import org.eclipse.jetty.http.HttpVersions;
+import org.eclipse.jetty.http.MimeTypes;
+import org.eclipse.jetty.http.Parser;
+import org.eclipse.jetty.http.PathMap;
+import org.eclipse.jetty.io.AbstractConnection;
+import org.eclipse.jetty.io.Buffer;
+import org.eclipse.jetty.io.BufferCache.CachedBuffer;
+import org.eclipse.jetty.io.Buffers;
+import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.EofException;
+import org.eclipse.jetty.io.RuntimeIOException;
+import org.eclipse.jetty.io.UncheckedPrintWriter;
+import org.eclipse.jetty.server.handler.ErrorHandler;
+import org.eclipse.jetty.server.nio.NIOConnector;
+import org.eclipse.jetty.server.ssl.SslConnector;
+import org.eclipse.jetty.util.QuotedStringTokenizer;
+import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.URIUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.resource.Resource;
+
+/**
+ * <p>A HttpConnection represents the connection of a HTTP client to the server
+ * and is created by an instance of a {@link Connector}. It's prime function is
+ * to associate {@link Request} and {@link Response} instances with a {@link EndPoint}.
+ * </p>
+ * <p>
+ * A connection is also the prime mechanism used by jetty to recycle objects without
+ * pooling.  The {@link Request},  {@link Response}, {@link HttpParser}, {@link HttpGenerator}
+ * and {@link HttpFields} instances are all recycled for the duraction of
+ * a connection. Where appropriate, allocated buffers are also kept associated
+ * with the connection via the parser and/or generator.
+ * </p>
+ * <p>
+ * The connection state is held by 3 separate state machines: The request state, the
+ * response state and the continuation state.  All three state machines must be driven
+ * to completion for every request, and all three can complete in any order.
+ * </p>
+ * <p>
+ * The HttpConnection support protocol upgrade.  If on completion of a request, the
+ * response code is 101 (switch protocols), then the org.eclipse.jetty.io.Connection
+ * request attribute is checked to see if there is a new Connection instance. If so,
+ * the new connection is returned from {@link #handle()} and is used for future
+ * handling of the underlying connection.   Note that for switching protocols that
+ * don't use 101 responses (eg CONNECT), the response should be sent and then the
+ * status code changed to 101 before returning from the handler.  Implementors
+ * of new Connection types should be careful to extract any buffered data from
+ * (HttpParser)http.getParser()).getHeaderBuffer() and
+ * (HttpParser)http.getParser()).getBodyBuffer() to initialise their new connection.
+ * </p>
+ *
+ */
+public abstract class AbstractHttpConnection  extends AbstractConnection
+{
+    private static final Logger LOG = Log.getLogger(AbstractHttpConnection.class);
+
+    private static final int UNKNOWN = -2;
+    private static final ThreadLocal<AbstractHttpConnection> __currentConnection = new ThreadLocal<AbstractHttpConnection>();
+
+    private int _requests;
+
+    protected final Connector _connector;
+    protected final Server _server;
+    protected final HttpURI _uri;
+
+    protected final Parser _parser;
+    protected final HttpFields _requestFields;
+    protected final Request _request;
+    protected volatile ServletInputStream _in;
+
+    protected final Generator _generator;
+    protected final HttpFields _responseFields;
+    protected final Response _response;
+    protected volatile Output _out;
+    protected volatile OutputWriter _writer;
+    protected volatile PrintWriter _printWriter;
+
+    int _include;
+
+    private Object _associatedObject; // associated object
+
+    private int _version = UNKNOWN;
+
+    private String _charset;
+    private boolean _expect = false;
+    private boolean _expect100Continue = false;
+    private boolean _expect102Processing = false;
+    private boolean _head = false;
+    private boolean _host = false;
+    private boolean _delayedHandling=false;
+    private boolean _earlyEOF = false;
+
+    /* ------------------------------------------------------------ */
+    public static AbstractHttpConnection getCurrentConnection()
+    {
+        return __currentConnection.get();
+    }
+
+    /* ------------------------------------------------------------ */
+    protected static void setCurrentConnection(AbstractHttpConnection connection)
+    {
+        __currentConnection.set(connection);
+    }
+
+    /* ------------------------------------------------------------ */
+    public AbstractHttpConnection(Connector connector, EndPoint endpoint, Server server)
+    {
+        super(endpoint);
+        _uri = StringUtil.__UTF8.equals(URIUtil.__CHARSET)?new HttpURI():new EncodedHttpURI(URIUtil.__CHARSET);
+        _connector = connector;
+        HttpBuffers ab = (HttpBuffers)_connector;
+        _parser = newHttpParser(ab.getRequestBuffers(), endpoint, new RequestHandler());
+        _requestFields = new HttpFields();
+        _responseFields = new HttpFields();
+        _request = new Request(this);
+        _response = new Response(this);
+        _generator = newHttpGenerator(ab.getResponseBuffers(), endpoint);
+        _generator.setSendServerVersion(server.getSendServerVersion());
+        _server = server;
+    }
+
+    /* ------------------------------------------------------------ */
+    protected AbstractHttpConnection(Connector connector, EndPoint endpoint, Server server,
+            Parser parser, Generator generator, Request request)
+    {
+        super(endpoint);
+
+        _uri = URIUtil.__CHARSET.equals(StringUtil.__UTF8)?new HttpURI():new EncodedHttpURI(URIUtil.__CHARSET);
+        _connector = connector;
+        _parser = parser;
+        _requestFields = new HttpFields();
+        _responseFields = new HttpFields();
+        _request = request;
+        _response = new Response(this);
+        _generator = generator;
+        _generator.setSendServerVersion(server.getSendServerVersion());
+        _server = server;
+    }
+
+    protected HttpParser newHttpParser(Buffers requestBuffers, EndPoint endpoint, HttpParser.EventHandler requestHandler)
+    {
+        return new HttpParser(requestBuffers, endpoint, requestHandler);
+    }
+
+    protected HttpGenerator newHttpGenerator(Buffers responseBuffers, EndPoint endPoint)
+    {
+        return new HttpGenerator(responseBuffers, endPoint);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return the parser used by this connection
+     */
+    public Parser getParser()
+    {
+        return _parser;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return the number of requests handled by this connection
+     */
+    public int getRequests()
+    {
+        return _requests;
+    }
+
+    /* ------------------------------------------------------------ */
+    public Server getServer()
+    {
+        return _server;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the associatedObject.
+     */
+    public Object getAssociatedObject()
+    {
+        return _associatedObject;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param associatedObject The associatedObject to set.
+     */
+    public void setAssociatedObject(Object associatedObject)
+    {
+        _associatedObject = associatedObject;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the connector.
+     */
+    public Connector getConnector()
+    {
+        return _connector;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the requestFields.
+     */
+    public HttpFields getRequestFields()
+    {
+        return _requestFields;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the responseFields.
+     */
+    public HttpFields getResponseFields()
+    {
+        return _responseFields;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Find out if the request supports CONFIDENTIAL security.
+     * @param request the incoming HTTP request
+     * @return the result of calling {@link Connector#isConfidential(Request)}, or false
+     * if there is no connector
+     */
+    public boolean isConfidential(Request request)
+    {
+        return _connector != null && _connector.isConfidential(request);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Find out if the request supports INTEGRAL security.
+     * @param request the incoming HTTP request
+     * @return the result of calling {@link Connector#isIntegral(Request)}, or false
+     * if there is no connector
+     */
+    public boolean isIntegral(Request request)
+    {
+        return _connector != null && _connector.isIntegral(request);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return <code>false</code> (this method is not yet implemented)
+     */
+    public boolean getResolveNames()
+    {
+        return _connector.getResolveNames();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the request.
+     */
+    public Request getRequest()
+    {
+        return _request;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the response.
+     */
+    public Response getResponse()
+    {
+        return _response;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Get the inputStream from the connection.
+     * <p>
+     * If the associated response has the Expect header set to 100 Continue,
+     * then accessing the input stream indicates that the handler/servlet
+     * is ready for the request body and thus a 100 Continue response is sent.
+     *
+     * @return The input stream for this connection.
+     * The stream will be created if it does not already exist.
+     * @throws IOException if the input stream cannot be retrieved
+     */
+    public ServletInputStream getInputStream() throws IOException
+    {
+        // If the client is expecting 100 CONTINUE, then send it now.
+        if (_expect100Continue)
+        {
+            // is content missing?
+            if (((HttpParser)_parser).getHeaderBuffer()==null || ((HttpParser)_parser).getHeaderBuffer().length()<2)
+            {
+                if (_generator.isCommitted())
+                    throw new IllegalStateException("Committed before 100 Continues");
+
+                ((HttpGenerator)_generator).send1xx(HttpStatus.CONTINUE_100);
+            }
+            _expect100Continue=false;
+        }
+
+        if (_in == null)
+            _in = new HttpInput(AbstractHttpConnection.this);
+        return _in;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The output stream for this connection. The stream will be created if it does not already exist.
+     */
+    public ServletOutputStream getOutputStream()
+    {
+        if (_out == null)
+            _out = new Output();
+        return _out;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param encoding the PrintWriter encoding
+     * @return A {@link PrintWriter} wrapping the {@link #getOutputStream output stream}. The writer is created if it
+     *    does not already exist.
+     */
+    public PrintWriter getPrintWriter(String encoding)
+    {
+        getOutputStream();
+        if (_writer==null)
+        {
+            _writer=new OutputWriter();
+            if (_server.isUncheckedPrintWriter())
+                _printWriter=new UncheckedPrintWriter(_writer);
+            else
+                _printWriter = new PrintWriter(_writer)
+                {
+                    public void close()
+                    {
+                        synchronized (lock)
+                        {
+                            try
+                            {
+                                out.close();
+                            }
+                            catch (IOException e)
+                            {
+                                setError();
+                            }
+                        }
+                    }
+                };
+        }
+        _writer.setCharacterEncoding(encoding);
+        return _printWriter;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isResponseCommitted()
+    {
+        return _generator.isCommitted();
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isEarlyEOF()
+    {
+        return _earlyEOF;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void reset()
+    {
+        _parser.reset();
+        _parser.returnBuffers(); // TODO maybe only on unhandle
+        _requestFields.clear();
+        _request.recycle();
+        _generator.reset();
+        _generator.returnBuffers();// TODO maybe only on unhandle
+        _responseFields.clear();
+        _response.recycle();
+        _uri.clear();
+        _writer=null;
+        _earlyEOF = false;
+    }
+
+    /* ------------------------------------------------------------ */
+    protected void handleRequest() throws IOException
+    {
+        boolean error = false;
+
+        String threadName=null;
+        Throwable async_exception=null;
+        try
+        {
+            if (LOG.isDebugEnabled())
+            {
+                threadName=Thread.currentThread().getName();
+                Thread.currentThread().setName(threadName+" - "+_uri);
+            }
+
+
+            // Loop here to handle async request redispatches.
+            // The loop is controlled by the call to async.unhandle in the
+            // finally block below.  If call is from a non-blocking connector,
+            // then the unhandle will return false only if an async dispatch has
+            // already happened when unhandle is called.   For a blocking connector,
+            // the wait for the asynchronous dispatch or timeout actually happens
+            // within the call to unhandle().
+
+            final Server server=_server;
+            boolean was_continuation=_request._async.isContinuation();
+            boolean handling=_request._async.handling() && server!=null && server.isRunning();
+            while (handling)
+            {
+                _request.setHandled(false);
+
+                String info=null;
+                try
+                {
+                    _uri.getPort();
+                    String path = null;
+
+                    try
+                    {
+                        path = _uri.getDecodedPath();
+                    }
+                    catch (Exception e)
+                    {
+                        LOG.warn("Failed UTF-8 decode for request path, trying ISO-8859-1");
+                        LOG.ignore(e);
+                        path = _uri.getDecodedPath(StringUtil.__ISO_8859_1);
+                    }
+
+                    info=URIUtil.canonicalPath(path);
+                    if (info==null && !_request.getMethod().equals(HttpMethods.CONNECT))
+                    {
+                        if (path==null && _uri.getScheme()!=null && _uri.getHost()!=null)
+                        {
+                            info="/";
+                            _request.setRequestURI("");
+                        }
+                        else
+                            throw new HttpException(400);
+                    }
+                    _request.setPathInfo(info);
+
+                    if (_out!=null)
+                        _out.reopen();
+
+                    if (_request._async.isInitial())
+                    {
+                        _request.setDispatcherType(DispatcherType.REQUEST);
+                        _connector.customize(_endp, _request);
+                        server.handle(this);
+                    }
+                    else
+                    {
+                        if (_request._async.isExpired()&&!was_continuation)
+                        {
+                            async_exception = (Throwable)_request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
+                            _response.setStatus(500,async_exception==null?"Async Timeout":"Async Exception");
+                            _request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE,new Integer(500));
+                            _request.setAttribute(RequestDispatcher.ERROR_MESSAGE, _response.getReason());
+                            _request.setDispatcherType(DispatcherType.ERROR);
+                            
+                            ErrorHandler eh = _request._async.getContextHandler().getErrorHandler();
+                            if (eh instanceof ErrorHandler.ErrorPageMapper)
+                            {
+                                String error_page=((ErrorHandler.ErrorPageMapper)eh).getErrorPage((HttpServletRequest)_request._async.getRequest());
+                                if (error_page!=null)
+                                { 
+                                    AsyncContinuation.AsyncEventState state = _request._async.getAsyncEventState();
+                                    state.setPath(error_page);
+                                }
+                            }
+                        }
+                        else
+                            _request.setDispatcherType(DispatcherType.ASYNC);
+                        server.handleAsync(this);
+                    }
+                }
+                catch (ContinuationThrowable e)
+                {
+                    LOG.ignore(e);
+                }
+                catch (EofException e)
+                {
+                    async_exception=e;
+                    LOG.debug(e);
+                    error=true;
+                    _request.setHandled(true);
+                    if (!_response.isCommitted())
+                        _generator.sendError(500, null, null, true);
+                }
+                catch (RuntimeIOException e)
+                {
+                    async_exception=e;
+                    LOG.debug(e);
+                    error=true;
+                    _request.setHandled(true);
+                }
+                catch (HttpException e)
+                {
+                    LOG.debug(e);
+                    error=true;
+                    _request.setHandled(true);
+                    _response.sendError(e.getStatus(), e.getReason());
+                }
+                catch (Throwable e)
+                {
+                    async_exception=e;
+                    LOG.warn(String.valueOf(_uri),e);
+                    error=true;
+                    _request.setHandled(true);
+                    _generator.sendError(info==null?400:500, null, null, true);
+                    
+                }
+                finally
+                {
+                    // Complete async requests 
+                    if (error && _request.isAsyncStarted())
+                        _request.getAsyncContinuation().errorComplete();
+                        
+                    was_continuation=_request._async.isContinuation();
+                    handling = !_request._async.unhandle() && server.isRunning() && _server!=null;
+                }
+            }
+        }
+        finally
+        {
+            if (threadName!=null)
+                Thread.currentThread().setName(threadName);
+
+            if (_request._async.isUncompleted())
+            {
+                
+                _request._async.doComplete(async_exception);
+
+                if (_expect100Continue)
+                {
+                    LOG.debug("100 continues not sent");
+                    // We didn't send 100 continues, but the latest interpretation
+                    // of the spec (see httpbis) is that the client will either
+                    // send the body anyway, or close.  So we no longer need to
+                    // do anything special here other than make the connection not persistent
+                    _expect100Continue = false;
+                    if (!_response.isCommitted())
+                        _generator.setPersistent(false);
+                }
+
+                if(_endp.isOpen())
+                {
+                    if (error)
+                    {
+                        _endp.shutdownOutput();
+                        _generator.setPersistent(false);
+                        if (!_generator.isComplete())
+                            _response.complete();
+                    }
+                    else
+                    {
+                        if (!_response.isCommitted() && !_request.isHandled())
+                            _response.sendError(HttpServletResponse.SC_NOT_FOUND);
+                        _response.complete();
+                        if (_generator.isPersistent())
+                            _connector.persist(_endp);
+                    }
+                }
+                else
+                {
+                    _response.complete();
+                }
+
+                _request.setHandled(true);
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public abstract Connection handle() throws IOException;
+
+    /* ------------------------------------------------------------ */
+    public void commitResponse(boolean last) throws IOException
+    {
+        if (!_generator.isCommitted())
+        {
+            _generator.setResponse(_response.getStatus(), _response.getReason());
+            try
+            {
+                // If the client was expecting 100 continues, but we sent something
+                // else, then we need to close the connection
+                if (_expect100Continue && _response.getStatus()!=100)
+                    _generator.setPersistent(false);
+                _generator.completeHeader(_responseFields, last);
+            }
+            catch(RuntimeException e)
+            {
+                LOG.warn("header full: " + e);
+
+                _response.reset();
+                _generator.reset();
+                _generator.setResponse(HttpStatus.INTERNAL_SERVER_ERROR_500,null);
+                _generator.completeHeader(_responseFields,Generator.LAST);
+                _generator.complete();
+                throw new HttpException(HttpStatus.INTERNAL_SERVER_ERROR_500);
+            }
+
+        }
+        if (last)
+            _generator.complete();
+    }
+
+    /* ------------------------------------------------------------ */
+    public void completeResponse() throws IOException
+    {
+        if (!_generator.isCommitted())
+        {
+            _generator.setResponse(_response.getStatus(), _response.getReason());
+            try
+            {
+                _generator.completeHeader(_responseFields, Generator.LAST);
+            }
+            catch(RuntimeException e)
+            {
+                LOG.warn("header full: "+e);
+                LOG.debug(e);
+
+                _response.reset();
+                _generator.reset();
+                _generator.setResponse(HttpStatus.INTERNAL_SERVER_ERROR_500,null);
+                _generator.completeHeader(_responseFields,Generator.LAST);
+                _generator.complete();
+                throw new HttpException(HttpStatus.INTERNAL_SERVER_ERROR_500);
+            }
+        }
+
+        _generator.complete();
+    }
+
+    /* ------------------------------------------------------------ */
+    public void flushResponse() throws IOException
+    {
+        try
+        {
+            commitResponse(Generator.MORE);
+            _generator.flushBuffer();
+        }
+        catch(IOException e)
+        {
+            throw (e instanceof EofException) ? e:new EofException(e);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public Generator getGenerator()
+    {
+        return _generator;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isIncluding()
+    {
+        return _include>0;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void include()
+    {
+        _include++;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void included()
+    {
+        _include--;
+        if (_out!=null)
+            _out.reopen();
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isIdle()
+    {
+        return _generator.isIdle() && (_parser.isIdle() || _delayedHandling);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.io.Connection#isSuspended()
+     */
+    public boolean isSuspended()
+    {
+        return _request.getAsyncContinuation().isSuspended();
+    }
+
+    /* ------------------------------------------------------------ */
+    public void onClose()
+    {
+        LOG.debug("closed {}",this);
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isExpecting100Continues()
+    {
+        return _expect100Continue;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isExpecting102Processing()
+    {
+        return _expect102Processing;
+    }
+
+    /* ------------------------------------------------------------ */
+    public int getMaxIdleTime()
+    {
+        if (_connector.isLowResources() && _endp.getMaxIdleTime()==_connector.getMaxIdleTime())
+            return _connector.getLowResourceMaxIdleTime();
+        if (_endp.getMaxIdleTime()>0)
+            return _endp.getMaxIdleTime();
+        return _connector.getMaxIdleTime();
+    }
+
+    /* ------------------------------------------------------------ */
+    public String toString()
+    {
+        return String.format("%s,g=%s,p=%s,r=%d",
+                super.toString(),
+                _generator,
+                _parser,
+                _requests);
+    }
+
+    /* ------------------------------------------------------------ */
+    protected void startRequest(Buffer method, Buffer uri, Buffer version) throws IOException
+    {
+        uri=uri.asImmutableBuffer();
+
+        _host = false;
+        _expect = false;
+        _expect100Continue=false;
+        _expect102Processing=false;
+        _delayedHandling=false;
+        _charset=null;
+
+        if(_request.getTimeStamp()==0)
+            _request.setTimeStamp(System.currentTimeMillis());
+        _request.setMethod(method.toString());
+
+        try
+        {
+            _head=false;
+            switch (HttpMethods.CACHE.getOrdinal(method))
+            {
+              case HttpMethods.CONNECT_ORDINAL:
+                  _uri.parseConnect(uri.array(), uri.getIndex(), uri.length());
+                  break;
+
+              case HttpMethods.HEAD_ORDINAL:
+                  _head=true;
+                  _uri.parse(uri.array(), uri.getIndex(), uri.length());
+                  break;
+
+              default:
+                  _uri.parse(uri.array(), uri.getIndex(), uri.length());
+            }
+
+            _request.setUri(_uri);
+
+            if (version==null)
+            {
+                _request.setProtocol(HttpVersions.HTTP_0_9);
+                _version=HttpVersions.HTTP_0_9_ORDINAL;
+            }
+            else
+            {
+                version= HttpVersions.CACHE.get(version);
+                if (version==null)
+                    throw new HttpException(HttpStatus.BAD_REQUEST_400,null);
+                _version = HttpVersions.CACHE.getOrdinal(version);
+                if (_version <= 0) _version = HttpVersions.HTTP_1_0_ORDINAL;
+                _request.setProtocol(version.toString());
+            }
+        }
+        catch (Exception e)
+        {
+            LOG.debug(e);
+            if (e instanceof HttpException)
+                throw (HttpException)e;
+            throw new HttpException(HttpStatus.BAD_REQUEST_400,null,e);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    protected void parsedHeader(Buffer name, Buffer value) throws IOException
+    {
+        int ho = HttpHeaders.CACHE.getOrdinal(name);
+        switch (ho)
+        {
+            case HttpHeaders.HOST_ORDINAL:
+                // TODO check if host matched a host in the URI.
+                _host = true;
+                break;
+
+            case HttpHeaders.EXPECT_ORDINAL:
+                if (_version>=HttpVersions.HTTP_1_1_ORDINAL)
+                {
+                    value = HttpHeaderValues.CACHE.lookup(value);
+                    switch(HttpHeaderValues.CACHE.getOrdinal(value))
+                    {
+                        case HttpHeaderValues.CONTINUE_ORDINAL:
+                            _expect100Continue=_generator instanceof HttpGenerator;
+                            break;
+
+                        case HttpHeaderValues.PROCESSING_ORDINAL:
+                            _expect102Processing=_generator instanceof HttpGenerator;
+                            break;
+
+                        default:
+                            String[] values = value.toString().split(",");
+                            for  (int i=0;values!=null && i<values.length;i++)
+                            {
+                                CachedBuffer cb=HttpHeaderValues.CACHE.get(values[i].trim());
+                                if (cb==null)
+                                    _expect=true;
+                                else
+                                {
+                                    switch(cb.getOrdinal())
+                                    {
+                                        case HttpHeaderValues.CONTINUE_ORDINAL:
+                                            _expect100Continue=_generator instanceof HttpGenerator;
+                                            break;
+                                        case HttpHeaderValues.PROCESSING_ORDINAL:
+                                            _expect102Processing=_generator instanceof HttpGenerator;
+                                            break;
+                                        default:
+                                            _expect=true;
+                                    }
+                                }
+                            }
+                    }
+                }
+                break;
+
+            case HttpHeaders.ACCEPT_ENCODING_ORDINAL:
+            case HttpHeaders.USER_AGENT_ORDINAL:
+                value = HttpHeaderValues.CACHE.lookup(value);
+                break;
+
+            case HttpHeaders.CONTENT_TYPE_ORDINAL:
+                value = MimeTypes.CACHE.lookup(value);
+                _charset=MimeTypes.getCharsetFromContentType(value);
+                break;
+        }
+
+        _requestFields.add(name, value);
+    }
+
+    /* ------------------------------------------------------------ */
+    protected void headerComplete() throws IOException
+    {
+        // Handle idle race
+        if (_endp.isOutputShutdown())
+        {
+            _endp.close();
+            return;
+        }
+        
+        _requests++;
+        _generator.setVersion(_version);
+        switch (_version)
+        {
+            case HttpVersions.HTTP_0_9_ORDINAL:
+                break;
+            case HttpVersions.HTTP_1_0_ORDINAL:
+                _generator.setHead(_head);
+                if (_parser.isPersistent())
+                {
+                    _responseFields.add(HttpHeaders.CONNECTION_BUFFER, HttpHeaderValues.KEEP_ALIVE_BUFFER);
+                    _generator.setPersistent(true);
+                }
+                else if (HttpMethods.CONNECT.equals(_request.getMethod()))
+                {
+                    _generator.setPersistent(true);
+                    _parser.setPersistent(true);
+                }
+
+                if (_server.getSendDateHeader())
+                    _generator.setDate(_request.getTimeStampBuffer());
+                break;
+
+            case HttpVersions.HTTP_1_1_ORDINAL:
+                _generator.setHead(_head);
+
+                if (!_parser.isPersistent())
+                {
+                    _responseFields.add(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.CLOSE_BUFFER);
+                    _generator.setPersistent(false);
+                }
+                if (_server.getSendDateHeader())
+                    _generator.setDate(_request.getTimeStampBuffer());
+
+                if (!_host)
+                {
+                    LOG.debug("!host {}",this);
+                    _generator.setResponse(HttpStatus.BAD_REQUEST_400, null);
+                    _responseFields.put(HttpHeaders.CONNECTION_BUFFER, HttpHeaderValues.CLOSE_BUFFER);
+                    _generator.completeHeader(_responseFields, true);
+                    _generator.complete();
+                    return;
+                }
+
+                if (_expect)
+                {
+                    LOG.debug("!expectation {}",this);
+                    _generator.setResponse(HttpStatus.EXPECTATION_FAILED_417, null);
+                    _responseFields.put(HttpHeaders.CONNECTION_BUFFER, HttpHeaderValues.CLOSE_BUFFER);
+                    _generator.completeHeader(_responseFields, true);
+                    _generator.complete();
+                    return;
+                }
+
+                break;
+            default:
+        }
+
+        if(_charset!=null)
+            _request.setCharacterEncodingUnchecked(_charset);
+
+        // Either handle now or wait for first content
+        if ((((HttpParser)_parser).getContentLength()<=0 && !((HttpParser)_parser).isChunking())||_expect100Continue)
+            handleRequest();
+        else
+            _delayedHandling=true;
+    }
+
+    /* ------------------------------------------------------------ */
+    protected void content(Buffer buffer) throws IOException
+    {
+        if (_delayedHandling)
+        {
+            _delayedHandling=false;
+            handleRequest();
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public void messageComplete(long contentLength) throws IOException
+    {
+        if (_delayedHandling)
+        {
+            _delayedHandling=false;
+            handleRequest();
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public void earlyEOF()
+    {
+        _earlyEOF = true;
+    }
+
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    private class RequestHandler extends HttpParser.EventHandler
+    {
+        /*
+         *
+         * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#startRequest(org.eclipse.io.Buffer,
+         *      org.eclipse.io.Buffer, org.eclipse.io.Buffer)
+         */
+        @Override
+        public void startRequest(Buffer method, Buffer uri, Buffer version) throws IOException
+        {
+            AbstractHttpConnection.this.startRequest(method, uri, version);
+        }
+
+        /*
+         * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#parsedHeaderValue(org.eclipse.io.Buffer)
+         */
+        @Override
+        public void parsedHeader(Buffer name, Buffer value) throws IOException
+        {
+            AbstractHttpConnection.this.parsedHeader(name, value);
+        }
+
+        /*
+         * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#headerComplete()
+         */
+        @Override
+        public void headerComplete() throws IOException
+        {
+            AbstractHttpConnection.this.headerComplete();
+        }
+
+        /* ------------------------------------------------------------ */
+        /*
+         * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#content(int, org.eclipse.io.Buffer)
+         */
+        @Override
+        public void content(Buffer ref) throws IOException
+        {
+            AbstractHttpConnection.this.content(ref);
+        }
+
+        /* ------------------------------------------------------------ */
+        /*
+         * (non-Javadoc)
+         *
+         * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#messageComplete(int)
+         */
+        @Override
+        public void messageComplete(long contentLength) throws IOException
+        {
+            AbstractHttpConnection.this.messageComplete(contentLength);
+        }
+
+        /* ------------------------------------------------------------ */
+        /*
+         * (non-Javadoc)
+         *
+         * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#startResponse(org.eclipse.io.Buffer, int,
+         *      org.eclipse.io.Buffer)
+         */
+        @Override
+        public void startResponse(Buffer version, int status, Buffer reason)
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug("Bad request!: "+version+" "+status+" "+reason);
+        }
+
+        /* ------------------------------------------------------------ */
+        /*
+         * (non-Javadoc)
+         *
+         * @see org.eclipse.jetty.server.server.HttpParser.EventHandler#earlyEOF()
+         */
+        @Override
+        public void earlyEOF()
+        {
+            AbstractHttpConnection.this.earlyEOF();
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    public class Output extends HttpOutput
+    {
+        Output()
+        {
+            super(AbstractHttpConnection.this);
+        }
+
+        /* ------------------------------------------------------------ */
+        /*
+         * @see java.io.OutputStream#close()
+         */
+        @Override
+        public void close() throws IOException
+        {
+            if (isClosed())
+                return;
+
+            if (!isIncluding() && !super._generator.isCommitted())
+                commitResponse(Generator.LAST);
+            else
+                flushResponse();
+
+            super.close();
+        }
+
+
+        /* ------------------------------------------------------------ */
+        /*
+         * @see java.io.OutputStream#flush()
+         */
+        @Override
+        public void flush() throws IOException
+        {
+            if (!super._generator.isCommitted())
+                commitResponse(Generator.MORE);
+            super.flush();
+        }
+
+        /* ------------------------------------------------------------ */
+        /*
+         * @see javax.servlet.ServletOutputStream#print(java.lang.String)
+         */
+        @Override
+        public void print(String s) throws IOException
+        {
+            if (isClosed())
+                throw new IOException("Closed");
+            PrintWriter writer=getPrintWriter(null);
+            writer.print(s);
+        }
+
+        /* ------------------------------------------------------------ */
+        public void sendResponse(Buffer response) throws IOException
+        {
+            ((HttpGenerator)super._generator).sendResponse(response);
+        }
+
+        /* ------------------------------------------------------------ */
+        public void sendContent(Object content) throws IOException
+        {
+            Resource resource=null;
+
+            if (isClosed())
+                throw new IOException("Closed");
+
+            if (super._generator.isWritten())
+                throw new IllegalStateException("!empty");
+
+            // Convert HTTP content to content
+            if (content instanceof HttpContent)
+            {
+                HttpContent httpContent = (HttpContent) content;
+                Buffer contentType = httpContent.getContentType();
+                if (contentType != null && !_responseFields.containsKey(HttpHeaders.CONTENT_TYPE_BUFFER))
+                {
+                    String enc = _response.getSetCharacterEncoding();
+                    if(enc==null)
+                        _responseFields.add(HttpHeaders.CONTENT_TYPE_BUFFER, contentType);
+                    else
+                    {
+                        if(contentType instanceof CachedBuffer)
+                        {
+                            CachedBuffer content_type = ((CachedBuffer)contentType).getAssociate(enc);
+                            if(content_type!=null)
+                                _responseFields.put(HttpHeaders.CONTENT_TYPE_BUFFER, content_type);
+                            else
+                            {
+                                _responseFields.put(HttpHeaders.CONTENT_TYPE_BUFFER,
+                                        contentType+";charset="+QuotedStringTokenizer.quoteIfNeeded(enc,";= "));
+                            }
+                        }
+                        else
+                        {
+                            _responseFields.put(HttpHeaders.CONTENT_TYPE_BUFFER,
+                                    contentType+";charset="+QuotedStringTokenizer.quoteIfNeeded(enc,";= "));
+                        }
+                    }
+                }
+                if (httpContent.getContentLength() > 0)
+                    _responseFields.putLongField(HttpHeaders.CONTENT_LENGTH_BUFFER, httpContent.getContentLength());
+                Buffer lm = httpContent.getLastModified();
+                long lml=httpContent.getResource().lastModified();
+                if (lm != null)
+                {
+                    _responseFields.put(HttpHeaders.LAST_MODIFIED_BUFFER, lm);
+                }
+                else if (httpContent.getResource()!=null)
+                {
+                    if (lml!=-1)
+                        _responseFields.putDateField(HttpHeaders.LAST_MODIFIED_BUFFER, lml);
+                }
+                
+                Buffer etag=httpContent.getETag();
+                if (etag!=null)
+                    _responseFields.put(HttpHeaders.ETAG_BUFFER,etag);
+
+                
+                boolean direct=_connector instanceof NIOConnector && ((NIOConnector)_connector).getUseDirectBuffers() && !(_connector instanceof SslConnector);
+                content = direct?httpContent.getDirectBuffer():httpContent.getIndirectBuffer();
+                if (content==null)
+                    content=httpContent.getInputStream();
+            }
+            else if (content instanceof Resource)
+            {
+                resource=(Resource)content;
+                _responseFields.putDateField(HttpHeaders.LAST_MODIFIED_BUFFER, resource.lastModified());
+                content=resource.getInputStream();
+            }
+
+            // Process content.
+            if (content instanceof Buffer)
+            {
+                super._generator.addContent((Buffer) content, Generator.LAST);
+                commitResponse(Generator.LAST);
+            }
+            else if (content instanceof InputStream)
+            {
+                InputStream in = (InputStream)content;
+
+                try
+                {
+                    int max = super._generator.prepareUncheckedAddContent();
+                    Buffer buffer = super._generator.getUncheckedBuffer();
+
+                    int len=buffer.readFrom(in,max);
+
+                    while (len>=0)
+                    {
+                        super._generator.completeUncheckedAddContent();
+                        _out.flush();
+
+                        max = super._generator.prepareUncheckedAddContent();
+                        buffer = super._generator.getUncheckedBuffer();
+                        len=buffer.readFrom(in,max);
+                    }
+                    super._generator.completeUncheckedAddContent();
+                    _out.flush();
+                }
+                finally
+                {
+                    if (resource!=null)
+                        resource.release();
+                    else
+                        in.close();
+                }
+            }
+            else
+                throw new IllegalArgumentException("unknown content type?");
+
+
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    public class OutputWriter extends HttpWriter
+    {
+        OutputWriter()
+        {
+            super(AbstractHttpConnection.this._out);
+        }
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/AsyncContinuation.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,1160 @@
+//
+//  ========================================================================
+//  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.server;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.AsyncEvent;
+import javax.servlet.AsyncListener;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+
+import org.eclipse.jetty.continuation.Continuation;
+import org.eclipse.jetty.continuation.ContinuationThrowable;
+import org.eclipse.jetty.continuation.ContinuationListener;
+import org.eclipse.jetty.io.AsyncEndPoint;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.server.handler.ContextHandler.Context;
+import org.eclipse.jetty.util.URIUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.Timeout;
+
+/* ------------------------------------------------------------ */
+/** Implementation of Continuation and AsyncContext interfaces
+ * 
+ */
+public class AsyncContinuation implements AsyncContext, Continuation
+{
+    private static final Logger LOG = Log.getLogger(AsyncContinuation.class);
+
+    private final static long DEFAULT_TIMEOUT=30000L;
+    
+    private final static ContinuationThrowable __exception = new ContinuationThrowable();
+    
+    // STATES:
+    //               handling()    suspend()     unhandle()    resume()       complete()  doComplete()
+    //                             startAsync()                dispatch()   
+    // IDLE          DISPATCHED      
+    // DISPATCHED                  ASYNCSTARTED  UNCOMPLETED
+    // ASYNCSTARTED                              ASYNCWAIT     REDISPATCHING  COMPLETING
+    // REDISPATCHING                             REDISPATCHED  
+    // ASYNCWAIT                                               REDISPATCH     COMPLETING
+    // REDISPATCH    REDISPATCHED
+    // REDISPATCHED                ASYNCSTARTED  UNCOMPLETED
+    // COMPLETING    UNCOMPLETED                 UNCOMPLETED
+    // UNCOMPLETED                                                                        COMPLETED
+    // COMPLETED
+    private static final int __IDLE=0;         // Idle request
+    private static final int __DISPATCHED=1;   // Request dispatched to filter/servlet
+    private static final int __ASYNCSTARTED=2; // Suspend called, but not yet returned to container
+    private static final int __REDISPATCHING=3;// resumed while dispatched
+    private static final int __ASYNCWAIT=4;    // Suspended and parked
+    private static final int __REDISPATCH=5;   // Has been scheduled
+    private static final int __REDISPATCHED=6; // Request redispatched to filter/servlet
+    private static final int __COMPLETING=7;   // complete while dispatched
+    private static final int __UNCOMPLETED=8;  // Request is completable
+    private static final int __COMPLETED=9;    // Request is complete
+    
+    /* ------------------------------------------------------------ */
+    protected AbstractHttpConnection _connection;
+    private List<AsyncListener> _lastAsyncListeners;
+    private List<AsyncListener> _asyncListeners;
+    private List<ContinuationListener> _continuationListeners;
+
+    /* ------------------------------------------------------------ */
+    private int _state;
+    private boolean _initial;
+    private boolean _resumed;
+    private boolean _expired;
+    private volatile boolean _responseWrapped;
+    private long _timeoutMs=DEFAULT_TIMEOUT;
+    private AsyncEventState _event;
+    private volatile long _expireAt;    
+    private volatile boolean _continuation;
+    
+    /* ------------------------------------------------------------ */
+    protected AsyncContinuation()
+    {
+        _state=__IDLE;
+        _initial=true;
+    }
+
+    /* ------------------------------------------------------------ */
+    protected void setConnection(final AbstractHttpConnection connection)
+    {
+        synchronized(this)
+        {
+            _connection=connection;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public void addListener(AsyncListener listener)
+    {
+        synchronized(this)
+        {
+            if (_asyncListeners==null)
+                _asyncListeners=new ArrayList<AsyncListener>();
+            _asyncListeners.add(listener);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public void addListener(AsyncListener listener,ServletRequest request, ServletResponse response)
+    {
+        synchronized(this)
+        {
+            // TODO handle the request/response ???
+            if (_asyncListeners==null)
+                _asyncListeners=new ArrayList<AsyncListener>();
+            _asyncListeners.add(listener);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public void addContinuationListener(ContinuationListener listener)
+    {
+        synchronized(this)
+        {
+            if (_continuationListeners==null)
+                _continuationListeners=new ArrayList<ContinuationListener>();
+            _continuationListeners.add(listener);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setTimeout(long ms)
+    {
+        synchronized(this)
+        {
+            _timeoutMs=ms;
+        }
+    } 
+
+    /* ------------------------------------------------------------ */
+    public long getTimeout()
+    {
+        synchronized(this)
+        {
+            return _timeoutMs;
+        }
+    } 
+
+    /* ------------------------------------------------------------ */
+    public AsyncEventState getAsyncEventState()
+    {
+        synchronized(this)
+        {
+            return _event;
+        }
+    } 
+   
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.continuation.Continuation#keepWrappers()
+     */
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.continuation.Continuation#isResponseWrapped()
+     */
+    public boolean isResponseWrapped()
+    {
+        return _responseWrapped;
+    }
+
+    /* ------------------------------------------------------------ */
+    /* (non-Javadoc)
+     * @see javax.servlet.ServletRequest#isInitial()
+     */
+    public boolean isInitial()
+    {
+        synchronized(this)
+        {
+            return _initial;
+        }
+    }
+    
+    public boolean isContinuation()
+    {
+        return _continuation;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /* (non-Javadoc)
+     * @see javax.servlet.ServletRequest#isSuspended()
+     */
+    public boolean isSuspended()
+    {
+        synchronized(this)
+        {
+            switch(_state)
+            {
+                case __ASYNCSTARTED:
+                case __REDISPATCHING:
+                case __COMPLETING:
+                case __ASYNCWAIT:
+                    return true;
+                    
+                default:
+                    return false;   
+            }
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    public boolean isSuspending()
+    {
+        synchronized(this)
+        {
+            switch(_state)
+            {
+                case __ASYNCSTARTED:
+                case __ASYNCWAIT:
+                    return true;
+                    
+                default:
+                    return false;   
+            }
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    public boolean isDispatchable()
+    {
+        synchronized(this)
+        {
+            switch(_state)
+            {
+                case __REDISPATCH:
+                case __REDISPATCHED:
+                case __REDISPATCHING:
+                case __COMPLETING:
+                    return true;
+                    
+                default:
+                    return false;   
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public String toString()
+    {
+        synchronized (this)
+        {
+            return super.toString()+"@"+getStatusString();
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public String getStatusString()
+    {
+        synchronized (this)
+        {
+            return
+            ((_state==__IDLE)?"IDLE":
+                (_state==__DISPATCHED)?"DISPATCHED":
+                    (_state==__ASYNCSTARTED)?"ASYNCSTARTED":
+                        (_state==__ASYNCWAIT)?"ASYNCWAIT":
+                            (_state==__REDISPATCHING)?"REDISPATCHING":
+                                (_state==__REDISPATCH)?"REDISPATCH":
+                                    (_state==__REDISPATCHED)?"REDISPATCHED":
+                                        (_state==__COMPLETING)?"COMPLETING":
+                                            (_state==__UNCOMPLETED)?"UNCOMPLETED":
+                                                (_state==__COMPLETED)?"COMPLETE":
+                                                    ("UNKNOWN?"+_state))+
+            (_initial?",initial":"")+
+            (_resumed?",resumed":"")+
+            (_expired?",expired":"");
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return false if the handling of the request should not proceed
+     */
+    protected boolean handling()
+    {
+        synchronized (this)
+        {
+            _continuation=false;
+            
+            switch(_state)
+            {
+                case __IDLE:
+                    _initial=true;
+                    _state=__DISPATCHED;
+                    if (_lastAsyncListeners!=null)
+                        _lastAsyncListeners.clear();
+                    if (_asyncListeners!=null)
+                        _asyncListeners.clear();
+                    else
+                    {
+                        _asyncListeners=_lastAsyncListeners;
+                        _lastAsyncListeners=null;
+                    }
+                    return true;
+                    
+                case __COMPLETING:
+                    _state=__UNCOMPLETED;
+                    return false;
+
+                case __ASYNCWAIT:
+                    return false;
+                    
+                case __REDISPATCH:
+                    _state=__REDISPATCHED;
+                    return true;
+
+                default:
+                    throw new IllegalStateException(this.getStatusString());
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /* (non-Javadoc)
+     * @see javax.servlet.ServletRequest#suspend(long)
+     */
+    private void doSuspend(final ServletContext context,
+            final ServletRequest request,
+            final ServletResponse response)
+    {
+        synchronized (this)
+        {
+            switch(_state)
+            {
+                case __DISPATCHED:
+                case __REDISPATCHED:
+                    _resumed=false;
+                    _expired=false;
+
+                    if (_event==null || request!=_event.getSuppliedRequest() || response != _event.getSuppliedResponse() || context != _event.getServletContext())
+                        _event=new AsyncEventState(context,request,response);
+                    else
+                    {
+                        _event._dispatchContext=null;
+                        _event._pathInContext=null;
+                    }
+                    _state=__ASYNCSTARTED;
+                    List<AsyncListener> recycle=_lastAsyncListeners;
+                    _lastAsyncListeners=_asyncListeners;
+                    _asyncListeners=recycle;
+                    if (_asyncListeners!=null)
+                        _asyncListeners.clear();
+                    break;
+
+                default:
+                    throw new IllegalStateException(this.getStatusString());
+            }
+        }
+        
+        if (_lastAsyncListeners!=null)
+        {
+            for (AsyncListener listener : _lastAsyncListeners)
+            {
+                try
+                {
+                    listener.onStartAsync(_event);
+                }
+                catch(Exception e)
+                {
+                    LOG.warn(e);
+                }
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Signal that the HttpConnection has finished handling the request.
+     * For blocking connectors, this call may block if the request has
+     * been suspended (startAsync called).
+     * @return true if handling is complete, false if the request should 
+     * be handled again (eg because of a resume that happened before unhandle was called)
+     */
+    protected boolean unhandle()
+    {
+        synchronized (this)
+        {
+            switch(_state)
+            {
+                case __REDISPATCHED:
+                case __DISPATCHED:
+                    _state=__UNCOMPLETED;
+                    return true;
+
+                case __IDLE:
+                    throw new IllegalStateException(this.getStatusString());
+
+                case __ASYNCSTARTED:
+                    _initial=false;
+                    _state=__ASYNCWAIT;
+                    scheduleTimeout(); // could block and change state.
+                    if (_state==__ASYNCWAIT)
+                        return true;
+                    else if (_state==__COMPLETING)
+                    {
+                        _state=__UNCOMPLETED;
+                        return true;
+                    }         
+                    _initial=false;
+                    _state=__REDISPATCHED;
+                    return false; 
+
+                case __REDISPATCHING:
+                    _initial=false;
+                    _state=__REDISPATCHED;
+                    return false; 
+
+                case __COMPLETING:
+                    _initial=false;
+                    _state=__UNCOMPLETED;
+                    return true;
+
+                default:
+                    throw new IllegalStateException(this.getStatusString());
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public void dispatch()
+    {
+        boolean dispatch=false;
+        synchronized (this)
+        {
+            switch(_state)
+            {
+                case __ASYNCSTARTED:
+                    _state=__REDISPATCHING;
+                    _resumed=true;
+                    return;
+
+                case __ASYNCWAIT:
+                    dispatch=!_expired;
+                    _state=__REDISPATCH;
+                    _resumed=true;
+                    break;
+                    
+                case __REDISPATCH:
+                    return;
+                    
+                default:
+                    throw new IllegalStateException(this.getStatusString());
+            }
+        }
+        
+        if (dispatch)
+        {
+            cancelTimeout();
+            scheduleDispatch();
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    protected void expired()
+    {
+        final List<ContinuationListener> cListeners;
+        final List<AsyncListener> aListeners;
+        synchronized (this)
+        {
+            switch(_state)
+            {
+                case __ASYNCSTARTED:
+                case __ASYNCWAIT:
+                    cListeners=_continuationListeners;
+                    aListeners=_asyncListeners;
+                    break;
+                default:
+                    cListeners=null;
+                    aListeners=null;
+                    return;
+            }
+            _expired=true;
+        }
+        
+        if (aListeners!=null)
+        {
+            for (AsyncListener listener : aListeners)
+            {
+                try
+                {
+                    listener.onTimeout(_event);
+                }
+                catch(Exception e)
+                {
+                    LOG.debug(e);
+                    _connection.getRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,e);
+                    break;
+                }
+            }
+        }
+        if (cListeners!=null)
+        {
+            for (ContinuationListener listener : cListeners)
+            {
+                try
+                {
+                    listener.onTimeout(this);
+                }
+                catch(Exception e)
+                {
+                    LOG.warn(e);
+                }
+            }
+        }
+        
+        synchronized (this)
+        {
+            switch(_state)
+            {
+                case __ASYNCSTARTED:
+                case __ASYNCWAIT:
+                    dispatch();
+                    break;
+                    
+                default:
+                    if (!_continuation)
+                        _expired=false;
+            }
+        }
+
+        scheduleDispatch();
+    }
+    
+    /* ------------------------------------------------------------ */
+    /* (non-Javadoc)
+     * @see javax.servlet.ServletRequest#complete()
+     */
+    public void complete()
+    {
+        // just like resume, except don't set _resumed=true;
+        boolean dispatch=false;
+        synchronized (this)
+        {
+            switch(_state)
+            {
+                case __DISPATCHED:
+                case __REDISPATCHED:
+                    throw new IllegalStateException(this.getStatusString());
+
+                case __ASYNCSTARTED:
+                    _state=__COMPLETING;
+                    return;
+                    
+                case __ASYNCWAIT:
+                    _state=__COMPLETING;
+                    dispatch=!_expired;
+                    break;
+                    
+                default:
+                    throw new IllegalStateException(this.getStatusString());
+            }
+        }
+        
+        if (dispatch)
+        {
+            cancelTimeout();
+            scheduleDispatch();
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    /* (non-Javadoc)
+     * @see javax.servlet.ServletRequest#complete()
+     */
+    public void errorComplete()
+    {
+        // just like complete except can overrule a prior dispatch call;
+        synchronized (this)
+        {
+            switch(_state)
+            {
+                case __REDISPATCHING:
+                case __ASYNCSTARTED:
+                    _state=__COMPLETING;
+                    _resumed=false;
+                    return;
+                    
+                case __COMPLETING:
+                    return;
+                    
+                default:
+                    throw new IllegalStateException(this.getStatusString());
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public <T extends AsyncListener> T createListener(Class<T> clazz) throws ServletException 
+    {
+        try
+        {
+            // TODO inject
+            return clazz.newInstance();
+        }
+        catch(Exception e)
+        {
+            throw new ServletException(e);
+        }
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /* (non-Javadoc)
+     * @see javax.servlet.ServletRequest#complete()
+     */
+    protected void doComplete(Throwable ex)
+    {
+        final List<ContinuationListener> cListeners;
+        final List<AsyncListener> aListeners;
+        synchronized (this)
+        {
+            switch(_state)
+            {
+                case __UNCOMPLETED:
+                    _state=__COMPLETED;
+                    cListeners=_continuationListeners;
+                    aListeners=_asyncListeners;
+                    break;
+                    
+                default:
+                    cListeners=null;
+                    aListeners=null;
+                    throw new IllegalStateException(this.getStatusString());
+            }
+        }
+        
+        if (aListeners!=null)
+        {
+            for (AsyncListener listener : aListeners)
+            {
+                try
+                {
+                    if (ex!=null)
+                    {
+                        _event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,ex);
+                        _event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_MESSAGE,ex.getMessage());
+                        listener.onError(_event);
+                    }
+                    else
+                        listener.onComplete(_event);
+                }
+                catch(Exception e)
+                {
+                    LOG.warn(e);
+                }
+            }
+        }
+        if (cListeners!=null)
+        {
+            for (ContinuationListener listener : cListeners)
+            {
+                try
+                {
+                    listener.onComplete(this);
+                }
+                catch(Exception e)
+                {
+                    LOG.warn(e);
+                }
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    protected void recycle()
+    {
+        synchronized (this)
+        {
+            switch(_state)
+            {
+                case __DISPATCHED:
+                case __REDISPATCHED:
+                    throw new IllegalStateException(getStatusString());
+                default:
+                    _state=__IDLE;
+            }
+            _initial = true;
+            _resumed=false;
+            _expired=false;
+            _responseWrapped=false;
+            cancelTimeout();
+            _timeoutMs=DEFAULT_TIMEOUT;
+            _continuationListeners=null;
+        }
+    }    
+    
+    /* ------------------------------------------------------------ */
+    public void cancel()
+    {
+        synchronized (this)
+        {
+            cancelTimeout();
+            _continuationListeners=null;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    protected void scheduleDispatch()
+    {
+        EndPoint endp=_connection.getEndPoint();
+        if (!endp.isBlocking())
+        {
+            ((AsyncEndPoint)endp).asyncDispatch();
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    protected void scheduleTimeout()
+    {
+        EndPoint endp=_connection.getEndPoint();
+        if (_timeoutMs>0)
+        {
+            if (endp.isBlocking())
+            {
+                synchronized(this)
+                {
+                    _expireAt = System.currentTimeMillis()+_timeoutMs;
+                    long wait=_timeoutMs;
+                    while (_expireAt>0 && wait>0 && _connection.getServer().isRunning())
+                    {
+                        try
+                        {
+                            this.wait(wait);
+                        }
+                        catch (InterruptedException e)
+                        {
+                            LOG.ignore(e);
+                        }
+                        wait=_expireAt-System.currentTimeMillis();
+                    }
+
+                    if (_expireAt>0 && wait<=0 && _connection.getServer().isRunning())
+                    {
+                        expired();
+                    }
+                }            
+            }
+            else
+            {
+                ((AsyncEndPoint)endp).scheduleTimeout(_event._timeout,_timeoutMs);
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    protected void cancelTimeout()
+    {
+        EndPoint endp=_connection.getEndPoint();
+        if (endp.isBlocking())
+        {
+            synchronized(this)
+            {
+                _expireAt=0;
+                this.notifyAll();
+            }
+        }
+        else 
+        {
+            final AsyncEventState event=_event;
+            if (event!=null)
+            {
+                ((AsyncEndPoint)endp).cancelTimeout(event._timeout);
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isCompleting()
+    {
+        synchronized (this)
+        {
+            return _state==__COMPLETING;
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    boolean isUncompleted()
+    {
+        synchronized (this)
+        {
+            return _state==__UNCOMPLETED;
+        }
+    } 
+    
+    /* ------------------------------------------------------------ */
+    public boolean isComplete()
+    {
+        synchronized (this)
+        {
+            return _state==__COMPLETED;
+        }
+    }
+
+
+    /* ------------------------------------------------------------ */
+    public boolean isAsyncStarted()
+    {
+        synchronized (this)
+        {
+            switch(_state)
+            {
+                case __ASYNCSTARTED:
+                case __REDISPATCHING:
+                case __REDISPATCH:
+                case __ASYNCWAIT:
+                    return true;
+
+                default:
+                    return false;
+            }
+        }
+    }
+
+
+    /* ------------------------------------------------------------ */
+    public boolean isAsync()
+    {
+        synchronized (this)
+        {
+            switch(_state)
+            {
+                case __IDLE:
+                case __DISPATCHED:
+                case __UNCOMPLETED:
+                case __COMPLETED:
+                    return false;
+
+                default:
+                    return true;
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public void dispatch(ServletContext context, String path)
+    {
+        _event._dispatchContext=context;
+        _event.setPath(path);
+        dispatch();
+    }
+
+    /* ------------------------------------------------------------ */
+    public void dispatch(String path)
+    {
+        _event.setPath(path);
+        dispatch();
+    }
+
+    /* ------------------------------------------------------------ */
+    public Request getBaseRequest()
+    {
+        return _connection.getRequest();
+    }
+    
+    /* ------------------------------------------------------------ */
+    public ServletRequest getRequest()
+    {
+        if (_event!=null)
+            return _event.getSuppliedRequest();
+        return _connection.getRequest();
+    }
+
+    /* ------------------------------------------------------------ */
+    public ServletResponse getResponse()
+    {
+        if (_responseWrapped && _event!=null && _event.getSuppliedResponse()!=null)
+            return _event.getSuppliedResponse();
+        return _connection.getResponse();
+    }
+
+    /* ------------------------------------------------------------ */
+    public void start(final Runnable run)
+    {
+        final AsyncEventState event=_event;
+        if (event!=null)
+        {
+            _connection.getServer().getThreadPool().dispatch(new Runnable()
+            {
+                public void run()
+                {
+                    ((Context)event.getServletContext()).getContextHandler().handle(run);
+                }
+            });
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean hasOriginalRequestAndResponse()
+    {
+        synchronized (this)
+        {
+            return (_event!=null && _event.getSuppliedRequest()==_connection._request && _event.getSuppliedResponse()==_connection._response);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public ContextHandler getContextHandler()
+    {
+        final AsyncEventState event=_event;
+        if (event!=null)
+            return ((Context)event.getServletContext()).getContextHandler();
+        return null;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see Continuation#isResumed()
+     */
+    public boolean isResumed()
+    {
+        synchronized (this)
+        {
+            return _resumed;
+        }
+    }
+    /* ------------------------------------------------------------ */
+    /**
+     * @see Continuation#isExpired()
+     */
+    public boolean isExpired()
+    {
+        synchronized (this)
+        {
+            return _expired;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see Continuation#resume()
+     */
+    public void resume()
+    {
+        dispatch();
+    }
+    
+
+
+    /* ------------------------------------------------------------ */
+    protected void startAsync(final ServletContext context,
+            final ServletRequest request,
+            final ServletResponse response)
+    {
+        synchronized (this)
+        {
+            _responseWrapped=!(response instanceof Response);
+            doSuspend(context,request,response);
+            if (request instanceof HttpServletRequest)
+            {
+                _event._pathInContext = URIUtil.addPaths(((HttpServletRequest)request).getServletPath(),((HttpServletRequest)request).getPathInfo());
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    protected void startAsync()
+    {
+        _responseWrapped=false;
+        _continuation=false;
+        doSuspend(_connection.getRequest().getServletContext(),_connection.getRequest(),_connection.getResponse());  
+    }
+
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @see Continuation#suspend()
+     */
+    public void suspend(ServletResponse response)
+    {
+        _continuation=true;
+        _responseWrapped=!(response instanceof Response);
+        doSuspend(_connection.getRequest().getServletContext(),_connection.getRequest(),response); 
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see Continuation#suspend()
+     */
+    public void suspend()
+    {
+        _responseWrapped=false;
+        _continuation=true;
+        doSuspend(_connection.getRequest().getServletContext(),_connection.getRequest(),_connection.getResponse());       
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.continuation.Continuation#getServletResponse()
+     */
+    public ServletResponse getServletResponse()
+    {
+        if (_responseWrapped && _event!=null && _event.getSuppliedResponse()!=null)
+            return _event.getSuppliedResponse();
+        return _connection.getResponse();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.continuation.Continuation#getAttribute(java.lang.String)
+     */
+    public Object getAttribute(String name)
+    {
+        return _connection.getRequest().getAttribute(name);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.continuation.Continuation#removeAttribute(java.lang.String)
+     */
+    public void removeAttribute(String name)
+    {
+        _connection.getRequest().removeAttribute(name);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.continuation.Continuation#setAttribute(java.lang.String, java.lang.Object)
+     */
+    public void setAttribute(String name, Object attribute)
+    {
+        _connection.getRequest().setAttribute(name,attribute);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.continuation.Continuation#undispatch()
+     */
+    public void undispatch()
+    {
+        if (isSuspended())
+        {
+            if (LOG.isDebugEnabled())
+                throw new ContinuationThrowable();
+            else
+                throw __exception;
+        }
+        throw new IllegalStateException("!suspended");
+    }
+
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    public class AsyncTimeout extends Timeout.Task implements Runnable
+    {
+            @Override
+            public void expired()
+            {
+                AsyncContinuation.this.expired();
+            }
+
+            @Override
+            public void run()
+            {
+                AsyncContinuation.this.expired();
+            }
+    }
+
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    public class AsyncEventState extends AsyncEvent
+    {
+        private final ServletContext _suspendedContext;
+        private ServletContext _dispatchContext;
+        private String _pathInContext;
+        private Timeout.Task _timeout=  new AsyncTimeout();
+        
+        public AsyncEventState(ServletContext context, ServletRequest request, ServletResponse response)
+        {
+            super(AsyncContinuation.this, request,response);
+            _suspendedContext=context;
+            // Get the base request So we can remember the initial paths
+            Request r=_connection.getRequest();
+ 
+            // If we haven't been async dispatched before
+            if (r.getAttribute(AsyncContext.ASYNC_REQUEST_URI)==null)
+            {
+                // We are setting these attributes during startAsync, when the spec implies that 
+                // they are only available after a call to AsyncContext.dispatch(...);
+                
+                // have we been forwarded before?
+                String uri=(String)r.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI);
+                if (uri!=null)
+                {
+                    r.setAttribute(AsyncContext.ASYNC_REQUEST_URI,uri);
+                    r.setAttribute(AsyncContext.ASYNC_CONTEXT_PATH,r.getAttribute(RequestDispatcher.FORWARD_CONTEXT_PATH));
+                    r.setAttribute(AsyncContext.ASYNC_SERVLET_PATH,r.getAttribute(RequestDispatcher.FORWARD_SERVLET_PATH));
+                    r.setAttribute(AsyncContext.ASYNC_PATH_INFO,r.getAttribute(RequestDispatcher.FORWARD_PATH_INFO));
+                    r.setAttribute(AsyncContext.ASYNC_QUERY_STRING,r.getAttribute(RequestDispatcher.FORWARD_QUERY_STRING));
+                }
+                else
+                {
+                    r.setAttribute(AsyncContext.ASYNC_REQUEST_URI,r.getRequestURI());
+                    r.setAttribute(AsyncContext.ASYNC_CONTEXT_PATH,r.getContextPath());
+                    r.setAttribute(AsyncContext.ASYNC_SERVLET_PATH,r.getServletPath());
+                    r.setAttribute(AsyncContext.ASYNC_PATH_INFO,r.getPathInfo());
+                    r.setAttribute(AsyncContext.ASYNC_QUERY_STRING,r.getQueryString());
+                }
+            }
+        }
+        
+        public ServletContext getSuspendedContext()
+        {
+            return _suspendedContext;
+        }
+        
+        public ServletContext getDispatchContext()
+        {
+            return _dispatchContext;
+        }
+        
+        public ServletContext getServletContext()
+        {
+            return _dispatchContext==null?_suspendedContext:_dispatchContext;
+        }
+        
+        public void setPath(String path)
+        {
+            _pathInContext=path;
+        }
+        
+        /* ------------------------------------------------------------ */
+        /**
+         * @return The path in the context
+         */
+        public String getPath()
+        {
+            return _pathInContext;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/AsyncHttpConnection.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,220 @@
+//
+//  ========================================================================
+//  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.server;
+
+import java.io.IOException;
+
+import org.eclipse.jetty.http.HttpException;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.io.AsyncEndPoint;
+import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.nio.AsyncConnection;
+import org.eclipse.jetty.io.nio.SelectChannelEndPoint;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+
+/* ------------------------------------------------------------ */
+/** Asychronous Server HTTP connection
+ *
+ */
+public class AsyncHttpConnection extends AbstractHttpConnection implements AsyncConnection
+{
+    private final static int NO_PROGRESS_INFO = Integer.getInteger("org.mortbay.jetty.NO_PROGRESS_INFO",100);
+    private final static int NO_PROGRESS_CLOSE = Integer.getInteger("org.mortbay.jetty.NO_PROGRESS_CLOSE",200);
+
+    private static final Logger LOG = Log.getLogger(AsyncHttpConnection.class);
+    private int _total_no_progress;
+    private final AsyncEndPoint _asyncEndp;
+    private boolean _readInterested = true;
+
+    public AsyncHttpConnection(Connector connector, EndPoint endpoint, Server server)
+    {
+        super(connector,endpoint,server);
+        _asyncEndp=(AsyncEndPoint)endpoint;
+    }
+
+    @Override
+    public Connection handle() throws IOException
+    {
+        Connection connection = this;
+        boolean some_progress=false;
+        boolean progress=true;
+
+        try
+        {
+            setCurrentConnection(this);
+
+            // don't check for idle while dispatched (unless blocking IO is done).
+            _asyncEndp.setCheckForIdle(false);
+
+
+            // While progress and the connection has not changed
+            while (progress && connection==this)
+            {
+                progress=false;
+                try
+                {
+                    // Handle resumed request
+                    if (_request._async.isAsync())
+                    {
+                       if (_request._async.isDispatchable())
+                           handleRequest();
+                    }
+                    // else Parse more input
+                    else if (!_parser.isComplete() && _parser.parseAvailable())
+                        progress=true;
+
+                    // Generate more output
+                    if (_generator.isCommitted() && !_generator.isComplete() && !_endp.isOutputShutdown() && !_request.getAsyncContinuation().isAsyncStarted())
+                        if (_generator.flushBuffer()>0)
+                            progress=true;
+
+                    // Flush output
+                    _endp.flush();
+
+                    // Has any IO been done by the endpoint itself since last loop
+                    if (_asyncEndp.hasProgressed())
+                        progress=true;
+                }
+                catch (HttpException e)
+                {
+                    if (LOG.isDebugEnabled())
+                    {
+                        LOG.debug("uri="+_uri);
+                        LOG.debug("fields="+_requestFields);
+                        LOG.debug(e);
+                    }
+                    progress=true;
+                    _generator.sendError(e.getStatus(), e.getReason(), null, true);
+                }
+                finally
+                {
+                    some_progress|=progress;
+                    //  Is this request/response round complete and are fully flushed?
+                    boolean parserComplete = _parser.isComplete();
+                    boolean generatorComplete = _generator.isComplete();
+                    boolean complete = parserComplete && generatorComplete;
+                    if (parserComplete)
+                    {
+                        if (generatorComplete)
+                        {
+                            // Reset the parser/generator
+                            progress=true;
+
+                            // look for a switched connection instance?
+                            if (_response.getStatus()==HttpStatus.SWITCHING_PROTOCOLS_101)
+                            {
+                                Connection switched=(Connection)_request.getAttribute("org.eclipse.jetty.io.Connection");
+                                if (switched!=null)
+                                    connection=switched;
+                            }
+
+                            reset();
+
+                            // TODO Is this still required?
+                            if (!_generator.isPersistent() && !_endp.isOutputShutdown())
+                            {
+                                LOG.warn("Safety net oshut!!!  IF YOU SEE THIS, PLEASE RAISE BUGZILLA");
+                                _endp.shutdownOutput();
+                            }
+                        }
+                        else
+                        {
+                            // We have finished parsing, but not generating so
+                            // we must not be interested in reading until we
+                            // have finished generating and we reset the generator
+                            _readInterested = false;
+                            LOG.debug("Disabled read interest while writing response {}", _endp);
+                        }
+                    }
+
+                    if (!complete && _request.getAsyncContinuation().isAsyncStarted())
+                    {
+                        // The request is suspended, so even though progress has been made,
+                        // exit the while loop by setting progress to false
+                        LOG.debug("suspended {}",this);
+                        progress=false;
+                    }
+                }
+            }
+        }
+        finally
+        {
+            setCurrentConnection(null);
+
+            // If we are not suspended
+            if (!_request.getAsyncContinuation().isAsyncStarted())
+            {
+                // return buffers
+                _parser.returnBuffers();
+                _generator.returnBuffers();
+
+                // reenable idle checking unless request is suspended
+                _asyncEndp.setCheckForIdle(true);
+            }
+
+            // Safety net to catch spinning
+            if (some_progress)
+                _total_no_progress=0;
+            else
+            {
+                _total_no_progress++;
+                if (NO_PROGRESS_INFO>0 && _total_no_progress%NO_PROGRESS_INFO==0 && (NO_PROGRESS_CLOSE<=0 || _total_no_progress< NO_PROGRESS_CLOSE))
+                    LOG.info("EndPoint making no progress: "+_total_no_progress+" "+_endp+" "+this);
+                if (NO_PROGRESS_CLOSE>0 && _total_no_progress==NO_PROGRESS_CLOSE)
+                {
+                    LOG.warn("Closing EndPoint making no progress: "+_total_no_progress+" "+_endp+" "+this);
+                    if (_endp instanceof SelectChannelEndPoint)
+                        ((SelectChannelEndPoint)_endp).getChannel().close();
+                }
+            }
+        }
+        return connection;
+    }
+
+    public void onInputShutdown() throws IOException
+    {
+        // If we don't have a committed response and we are not suspended
+        if (_generator.isIdle() && !_request.getAsyncContinuation().isSuspended())
+        {
+            // then no more can happen, so close.
+            _endp.close();
+        }
+
+        // Make idle parser seek EOF
+        if (_parser.isIdle())
+            _parser.setPersistent(false);
+    }
+
+    @Override
+    public void reset()
+    {
+        _readInterested = true;
+        LOG.debug("Enabled read interest {}", _endp);
+        super.reset();
+    }
+
+    @Override
+    public boolean isSuspended()
+    {
+        return !_readInterested || super.isSuspended();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/AsyncNCSARequestLog.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,130 @@
+//
+//  ========================================================================
+//  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.server;
+
+import org.eclipse.jetty.util.BlockingArrayQueue;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+import java.io.IOException;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+
+/* ------------------------------------------------------------ */
+/**
+ * An asynchronously writing NCSA Request Log
+ */
+public class AsyncNCSARequestLog extends NCSARequestLog
+{
+    private static final Logger LOG = Log.getLogger(AsyncNCSARequestLog.class);
+    private final BlockingQueue<String> _queue;
+    private transient WriterThread _thread;
+    private boolean _warnedFull;
+
+    public AsyncNCSARequestLog()
+    {
+        this(null,null);
+    }
+    
+    public AsyncNCSARequestLog(BlockingQueue<String> queue)
+    {
+        this(null,queue);
+    }
+
+    public AsyncNCSARequestLog(String filename)
+    {
+        this(filename,null);
+    }
+    
+    public AsyncNCSARequestLog(String filename,BlockingQueue<String> queue)
+    {
+        super(filename);
+        if (queue==null)
+            queue=new BlockingArrayQueue<String>(1024);
+        _queue=queue;
+    }
+
+    private class WriterThread extends Thread
+    {
+        WriterThread()
+        {
+            setName("AsyncNCSARequestLog@"+Integer.toString(AsyncNCSARequestLog.this.hashCode(),16));
+        }
+        
+        @Override
+        public void run()
+        {
+            while (isRunning())
+            {
+                try
+                {
+                    String log = _queue.poll(10,TimeUnit.SECONDS);
+                    if (log!=null)
+                        AsyncNCSARequestLog.super.write(log);
+                    
+                    while(!_queue.isEmpty())
+                    {
+                        log=_queue.poll();
+                        if (log!=null)
+                            AsyncNCSARequestLog.super.write(log);
+                    }
+                }
+                catch (IOException e)
+                {
+                    LOG.warn(e);
+                }
+                catch (InterruptedException e)
+                {
+                    LOG.ignore(e);
+                }
+            }
+        }
+    }
+
+    @Override
+    protected synchronized void doStart() throws Exception
+    {
+        super.doStart();
+        _thread = new WriterThread();
+        _thread.start();
+    }
+
+    @Override
+    protected void doStop() throws Exception
+    {
+        _thread.interrupt();
+        _thread.join();
+        super.doStop();
+        _thread=null;
+    }
+
+    @Override
+    protected void write(String log) throws IOException
+    {
+        if (!_queue.offer(log))
+        {
+            if (_warnedFull)
+                LOG.warn("Log Queue overflow");
+            _warnedFull=true;
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/Authentication.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,155 @@
+//
+//  ========================================================================
+//  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.server;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+
+/* ------------------------------------------------------------ */
+/** The Authentication state of a request.
+ * <p>
+ * The Authentication state can be one of several sub-types that
+ * reflects where the request is in the many different authentication
+ * cycles. Authentication might not yet be checked or it might be checked
+ * and failed, checked and deferred or succeeded. 
+ * 
+ */
+public interface Authentication
+{
+    /* ------------------------------------------------------------ */
+    /** A successful Authentication with User information.
+     */
+    public interface User extends Authentication
+    {
+        String getAuthMethod();
+        UserIdentity getUserIdentity(); 
+        boolean isUserInRole(UserIdentity.Scope scope,String role);
+        void logout();
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** A wrapped authentication with methods provide the
+     * wrapped request/response for use by the application
+     */
+    public interface Wrapped extends Authentication
+    {
+        HttpServletRequest getHttpServletRequest();
+        HttpServletResponse getHttpServletResponse();
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** A deferred authentication with methods to progress 
+     * the authentication process.
+     */
+    public interface Deferred extends Authentication
+    {
+        /* ------------------------------------------------------------ */
+        /** Authenticate if possible without sending a challenge.
+         * This is used to check credentials that have been sent for 
+         * non-manditory authentication.
+         * @return The new Authentication state.
+         */
+        Authentication authenticate(ServletRequest request);
+
+        /* ------------------------------------------------------------ */
+        /** Authenticate and possibly send a challenge.
+         * This is used to initiate authentication for previously 
+         * non-manditory authentication.
+         * @return The new Authentication state.
+         */
+        Authentication authenticate(ServletRequest request,ServletResponse response);
+        
+        
+        /* ------------------------------------------------------------ */
+        /** Login with the LOGIN authenticator
+         * @param username
+         * @param password
+         * @return The new Authentication state
+         */
+        Authentication login(String username,Object password,ServletRequest request);
+    }
+
+    
+    /* ------------------------------------------------------------ */
+    /** Authentication Response sent state.
+     * Responses are sent by authenticators either to issue an
+     * authentication challenge or on successful authentication in
+     * order to redirect the user to the original URL.
+     */
+    public interface ResponseSent extends Authentication
+    { 
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** An Authentication Challenge has been sent.
+     */
+    public interface Challenge extends ResponseSent
+    { 
+    }
+
+    /* ------------------------------------------------------------ */
+    /** An Authentication Failure has been sent.
+     */
+    public interface Failure extends ResponseSent
+    { 
+    }
+
+    public interface SendSuccess extends ResponseSent
+    {
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Unauthenticated state.
+     * <p> 
+     * This convenience instance is for non mandatory authentication where credentials
+     * have been presented and checked, but failed authentication. 
+     */
+    public final static Authentication UNAUTHENTICATED = new Authentication(){@Override
+    public String toString(){return "UNAUTHENTICATED";}};
+
+    /* ------------------------------------------------------------ */
+    /** Authentication not checked
+     * <p>
+     * This convenience instance us for non mandatory authentication when no 
+     * credentials are present to be checked.
+     */
+    public final static Authentication NOT_CHECKED = new Authentication(){@Override
+    public String toString(){return "NOT CHECKED";}};
+
+    /* ------------------------------------------------------------ */
+    /** Authentication challenge sent.
+     * <p>
+     * This convenience instance is for when an authentication challenge has been sent.
+     */
+    public final static Authentication SEND_CONTINUE = new Authentication.Challenge(){@Override
+    public String toString(){return "CHALLENGE";}};
+
+    /* ------------------------------------------------------------ */
+    /** Authentication failure sent.
+     * <p>
+     * This convenience instance is for when an authentication failure has been sent.
+     */
+    public final static Authentication SEND_FAILURE = new Authentication.Failure(){@Override
+    public String toString(){return "FAILURE";}};
+    public final static Authentication SEND_SUCCESS = new SendSuccess(){@Override
+    public String toString(){return "SEND_SUCCESS";}};
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/BlockingHttpConnection.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,138 @@
+//
+//  ========================================================================
+//  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.server;
+
+import java.io.IOException;
+
+import org.eclipse.jetty.http.Generator;
+import org.eclipse.jetty.http.HttpException;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.Parser;
+import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+
+/* ------------------------------------------------------------ */
+/** Blocking Server HTTP Connection
+ */
+public class BlockingHttpConnection extends AbstractHttpConnection
+{
+    private static final Logger LOG = Log.getLogger(BlockingHttpConnection.class);
+
+    public BlockingHttpConnection(Connector connector, EndPoint endpoint, Server server)
+    {
+        super(connector,endpoint,server);
+    }
+
+    public BlockingHttpConnection(Connector connector, EndPoint endpoint, Server server, Parser parser, Generator generator, Request request)
+    {
+        super(connector,endpoint,server,parser,generator,request);
+    }
+
+    @Override
+    protected void handleRequest() throws IOException
+    {
+        super.handleRequest();
+    }
+
+    public Connection handle() throws IOException
+    {
+        Connection connection = this;
+
+        try
+        {
+            setCurrentConnection(this);
+
+            // do while the endpoint is open
+            // AND the connection has not changed
+            while (_endp.isOpen() && connection==this)
+            {
+                try
+                {
+                    // If we are not ended then parse available
+                    if (!_parser.isComplete() && !_endp.isInputShutdown())
+                        _parser.parseAvailable();
+
+                    // Do we have more generating to do?
+                    // Loop here because some writes may take multiple steps and
+                    // we need to flush them all before potentially blocking in the
+                    // next loop.
+                    if (_generator.isCommitted() && !_generator.isComplete() && !_endp.isOutputShutdown())
+                        _generator.flushBuffer();
+
+                    // Flush buffers
+                    _endp.flush();
+                }
+                catch (HttpException e)
+                {
+                    if (LOG.isDebugEnabled())
+                    {
+                        LOG.debug("uri="+_uri);
+                        LOG.debug("fields="+_requestFields);
+                        LOG.debug(e);
+                    }
+                    _generator.sendError(e.getStatus(), e.getReason(), null, true);
+                    _parser.reset();
+                    _endp.shutdownOutput();
+                }
+                finally
+                {
+                    //  Is this request/response round complete and are fully flushed?
+                    if (_parser.isComplete() && _generator.isComplete())
+                    {
+                        // Reset the parser/generator
+                        reset();
+
+                        // look for a switched connection instance?
+                        if (_response.getStatus()==HttpStatus.SWITCHING_PROTOCOLS_101)
+                        {
+                            Connection switched=(Connection)_request.getAttribute("org.eclipse.jetty.io.Connection");
+                            if (switched!=null)
+                                connection=switched;
+                        }
+
+                        // TODO Is this required?
+                        if (!_generator.isPersistent() && !_endp.isOutputShutdown())
+                        {
+                            LOG.warn("Safety net oshut!!! Please open a bugzilla");
+                            _endp.shutdownOutput();
+                        }
+                    }
+                    
+                    // If we don't have a committed response and we are not suspended
+                    if (_endp.isInputShutdown() && _generator.isIdle() && !_request.getAsyncContinuation().isSuspended())
+                    {
+                        // then no more can happen, so close.
+                        _endp.close();
+                    }
+                }
+            }
+
+            return connection;
+        }
+        finally
+        {
+            setCurrentConnection(null);
+            _parser.returnBuffers();
+            _generator.returnBuffers();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/Connector.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,387 @@
+//
+//  ========================================================================
+//  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.server;
+
+import java.io.IOException;
+
+import org.eclipse.jetty.io.Buffers;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.util.component.LifeCycle;
+import org.eclipse.jetty.util.thread.ThreadPool;
+
+/** HTTP Connector.
+ * Implementations of this interface provide connectors for the HTTP protocol.
+ * A connector receives requests (normally from a socket) and calls the 
+ * handle method of the Handler object.  These operations are performed using
+ * threads from the ThreadPool set on the connector.
+ * 
+ * When a connector is registered with an instance of Server, then the server
+ * will set itself as both the ThreadPool and the Handler.  Note that a connector
+ * can be used without a Server if a thread pool and handler are directly provided.
+ * 
+ * 
+ * 
+ */
+/**
+ * @author gregw
+ *
+ */
+public interface Connector extends LifeCycle
+{ 
+    /* ------------------------------------------------------------ */
+    /**
+     * @return the name of the connector. Defaults to the HostName:port
+     */
+    String getName();
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Opens the connector 
+     * @throws IOException
+     */
+    void open() throws IOException;
+
+    /* ------------------------------------------------------------ */
+    void close() throws IOException;
+
+    /* ------------------------------------------------------------ */
+    void setServer(Server server);
+    
+    /* ------------------------------------------------------------ */
+    Server getServer();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the request header buffer size in bytes.
+     */
+    int getRequestHeaderSize();
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Set the size of the buffer to be used for request headers.
+     * @param size The size in bytes.
+     */
+    void setRequestHeaderSize(int size);
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the response header buffer size in bytes.
+     */
+    int getResponseHeaderSize();
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Set the size of the buffer to be used for request headers.
+     * @param size The size in bytes.
+     */
+    void setResponseHeaderSize(int size);
+    
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return factory for request buffers
+     */
+    Buffers getRequestBuffers();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return factory for response buffers
+     */
+    Buffers getResponseBuffers();
+    
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the requestBufferSize.
+     */
+    int getRequestBufferSize();
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Set the size of the content buffer for receiving requests. 
+     * These buffers are only used for active connections that have
+     * requests with bodies that will not fit within the header buffer.
+     * @param requestBufferSize The requestBufferSize to set.
+     */
+    void setRequestBufferSize(int requestBufferSize);
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the responseBufferSize.
+     */
+    int getResponseBufferSize();
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Set the size of the content buffer for sending responses. 
+     * These buffers are only used for active connections that are sending 
+     * responses with bodies that will not fit within the header buffer.
+     * @param responseBufferSize The responseBufferSize to set.
+     */
+    void setResponseBufferSize(int responseBufferSize);
+    
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The port to use when redirecting a request if a data constraint of integral is 
+     * required. See {@link org.eclipse.jetty.util.security.Constraint#getDataConstraint()}
+     */
+    int getIntegralPort();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The schema to use when redirecting a request if a data constraint of integral is 
+     * required. See {@link org.eclipse.jetty.util.security.Constraint#getDataConstraint()}
+     */
+    String getIntegralScheme();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param request A request
+     * @return true if the request is integral. This normally means the https schema has been used.
+     */
+    boolean isIntegral(Request request);
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The port to use when redirecting a request if a data constraint of confidential is 
+     * required. See {@link org.eclipse.jetty.util.security.Constraint#getDataConstraint()}
+     */
+    int getConfidentialPort();
+    
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The schema to use when redirecting a request if a data constraint of confidential is 
+     * required. See {@link org.eclipse.jetty.util.security.Constraint#getDataConstraint()}
+     */
+    String getConfidentialScheme();
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @param request A request
+     * @return true if the request is confidential. This normally means the https schema has been used.
+     */
+    boolean isConfidential(Request request);
+
+    /* ------------------------------------------------------------ */
+    /** Customize a request for an endpoint.
+     * Called on every request to allow customization of the request for
+     * the particular endpoint (eg security properties from a SSL connection).
+     * @param endpoint
+     * @param request
+     * @throws IOException
+     */
+    void customize(EndPoint endpoint, Request request) throws IOException;
+
+    /* ------------------------------------------------------------ */
+    /** Persist an endpoint.
+     * Called after every request if the connection is to remain open.
+     * @param endpoint
+     * @throws IOException
+     */
+    void persist(EndPoint endpoint) throws IOException;
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The hostname representing the interface to which 
+     * this connector will bind, or null for all interfaces.
+     */
+    String getHost();
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Set the hostname of the interface to bind to.
+     * @param hostname The hostname representing the interface to which 
+     * this connector will bind, or null for all interfaces.
+     */
+    void setHost(String hostname);
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param port The port to listen of for connections or 0 if any available
+     * port may be used.
+     */
+    void setPort(int port);
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The configured port for the connector or 0 if any available
+     * port may be used.
+     */
+    int getPort();
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The actual port the connector is listening on or
+     * -1 if it has not been opened, or -2 if it has been closed.
+     */
+    int getLocalPort();
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Max Idle time for connections in milliseconds
+     */
+    int getMaxIdleTime();
+    
+    /**
+     * @param ms Max Idle time for connections in milliseconds
+     */
+    void setMaxIdleTime(int ms);
+    
+    /* ------------------------------------------------------------ */
+    int getLowResourceMaxIdleTime();
+    void setLowResourceMaxIdleTime(int ms);
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @return the underlying socket, channel, buffer etc. for the connector.
+     */
+    Object getConnection();
+    
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @return true if names resolution should be done.
+     */
+    boolean getResolveNames();
+    
+    
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Get the number of requests handled by this connector
+     * since last call of statsReset(). If setStatsOn(false) then this
+     * is undefined.
+     */
+    public int getRequests();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the connectionsDurationTotal.
+     */
+    public long getConnectionsDurationTotal();
+
+    /* ------------------------------------------------------------ */
+    /** 
+     * @return Number of connections accepted by the server since
+     * statsReset() called. Undefined if setStatsOn(false).
+     */
+    public int getConnections() ;
+
+    /* ------------------------------------------------------------ */
+    /** 
+     * @return Number of connections currently open that were opened
+     * since statsReset() called. Undefined if setStatsOn(false).
+     */
+    public int getConnectionsOpen() ;
+
+    /* ------------------------------------------------------------ */
+    /** 
+     * @return Maximum number of connections opened simultaneously
+     * since statsReset() called. Undefined if setStatsOn(false).
+     */
+    public int getConnectionsOpenMax() ;
+
+    /* ------------------------------------------------------------ */
+    /** 
+     * @return Maximum duration in milliseconds of an open connection
+     * since statsReset() called. Undefined if setStatsOn(false).
+     */
+    public long getConnectionsDurationMax();
+
+    /* ------------------------------------------------------------ */
+    /** 
+     * @return Mean duration in milliseconds of open connections
+     * since statsReset() called. Undefined if setStatsOn(false).
+     */
+    public double getConnectionsDurationMean() ;
+
+    /* ------------------------------------------------------------ */
+    /** 
+     * @return Standard deviation of duration in milliseconds of
+     * open connections since statsReset() called. Undefined if
+     * setStatsOn(false).
+     */
+    public double getConnectionsDurationStdDev() ;
+
+    /* ------------------------------------------------------------ */
+    /** 
+     * @return Mean number of requests per connection
+     * since statsReset() called. Undefined if setStatsOn(false).
+     */
+    public double getConnectionsRequestsMean() ;
+
+    /* ------------------------------------------------------------ */
+    /** 
+     * @return Standard Deviation of number of requests per connection
+     * since statsReset() called. Undefined if setStatsOn(false).
+     */
+    public double getConnectionsRequestsStdDev() ;
+
+    /* ------------------------------------------------------------ */
+    /** 
+     * @return Maximum number of requests per connection
+     * since statsReset() called. Undefined if setStatsOn(false).
+     */
+    public int getConnectionsRequestsMax();
+
+    /* ------------------------------------------------------------ */
+    /** Reset statistics.
+     */
+    public void statsReset();
+    
+    /* ------------------------------------------------------------ */
+    public void setStatsOn(boolean on);
+    
+    /* ------------------------------------------------------------ */
+    /** 
+     * @return True if statistics collection is turned on.
+     */
+    public boolean getStatsOn();
+    
+    /* ------------------------------------------------------------ */
+    /** 
+     * @return Timestamp stats were started at.
+     */
+    public long getStatsOnMs();
+    
+
+    /* ------------------------------------------------------------ */
+    /** Check if low on resources.
+     * For most connectors, low resources is measured by calling 
+     * {@link ThreadPool#isLowOnThreads()} on the connector threadpool
+     * or the server threadpool if there is no connector threadpool.
+     * <p>
+     * For blocking connectors, low resources is used to trigger
+     * usage of {@link #getLowResourceMaxIdleTime()} for the timeout
+     * of an idle connection.
+     * <p>
+     * for non-blocking connectors, the number of connections is
+     * used instead of this method, to select the timeout of an 
+     * idle connection.
+     * <p>
+     * For all connectors, low resources is used to trigger the 
+     * usage of {@link #getLowResourceMaxIdleTime()} for read and 
+     * write operations.
+     * 
+     * @return true if this connector is low on resources.
+     */
+    public boolean isLowResources();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/CookieCutter.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,335 @@
+//
+//  ========================================================================
+//  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.server;
+import java.util.Locale;
+
+import javax.servlet.http.Cookie;
+
+import org.eclipse.jetty.util.LazyList;
+import org.eclipse.jetty.util.QuotedStringTokenizer;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+
+/* ------------------------------------------------------------ */
+/** Cookie parser
+ * <p>Optimized stateful cookie parser.  Cookies fields are added with the
+ * {@link #addCookieField(String)} method and parsed on the next subsequent
+ * call to {@link #getCookies()}.
+ * If the added fields are identical to those last added (as strings), then the 
+ * cookies are not re parsed.
+ * 
+ *
+ */
+public class CookieCutter
+{
+    private static final Logger LOG = Log.getLogger(CookieCutter.class);
+
+
+    private Cookie[] _cookies;
+    private Cookie[] _lastCookies;
+    Object _lazyFields;
+    int _fields;
+    
+    public CookieCutter()
+    {  
+    }
+    
+    public Cookie[] getCookies()
+    {
+        if (_cookies!=null)
+            return _cookies;
+        
+        if (_lastCookies!=null &&
+            _lazyFields!=null &&
+            _fields==LazyList.size(_lazyFields))
+            _cookies=_lastCookies;
+        else
+            parseFields();
+        _lastCookies=_cookies;
+        return _cookies;
+    }
+    
+    public void setCookies(Cookie[] cookies)
+    {
+        _cookies=cookies;
+        _lastCookies=null;
+        _lazyFields=null;
+        _fields=0;
+    }
+    
+    public void reset()
+    {
+        _cookies=null;
+        _fields=0;
+    }
+    
+    public void addCookieField(String f)
+    {
+        if (f==null)
+            return;
+        f=f.trim();
+        if (f.length()==0)
+            return;
+            
+        if (LazyList.size(_lazyFields)>_fields)
+        {
+            if (f.equals(LazyList.get(_lazyFields,_fields)))
+            {
+                _fields++;
+                return;
+            }
+            
+            while (LazyList.size(_lazyFields)>_fields)
+                _lazyFields=LazyList.remove(_lazyFields,_fields);
+        }
+        _cookies=null;
+        _lastCookies=null;
+        _lazyFields=LazyList.add(_lazyFields,_fields++,f);
+    }
+    
+    
+    protected void parseFields()
+    {
+        _lastCookies=null;
+        _cookies=null;
+        
+        Object cookies = null;
+
+        int version = 0;
+
+        // delete excess fields
+        while (LazyList.size(_lazyFields)>_fields)
+            _lazyFields=LazyList.remove(_lazyFields,_fields);
+        
+        // For each cookie field
+        for (int f=0;f<_fields;f++)
+        {
+            String hdr = LazyList.get(_lazyFields,f);
+            
+            // Parse the header
+            String name = null;
+            String value = null;
+
+            Cookie cookie = null;
+
+            boolean invalue=false;
+            boolean quoted=false;
+            boolean escaped=false;
+            int tokenstart=-1;
+            int tokenend=-1;
+            for (int i = 0, length = hdr.length(), last=length-1; i < length; i++)
+            {
+                char c = hdr.charAt(i);
+                
+                // Handle quoted values for name or value
+                if (quoted)
+                {
+                    if (escaped)
+                    {
+                        escaped=false;
+                        continue;
+                    }
+                    
+                    switch (c)
+                    {
+                        case '"':
+                            tokenend=i;
+                            quoted=false;
+
+                            // handle quote as last character specially
+                            if (i==last)
+                            {
+                                if (invalue)
+                                    value = hdr.substring(tokenstart, tokenend+1);
+                                else
+                                {
+                                    name = hdr.substring(tokenstart, tokenend+1);
+                                    value = "";
+                                }
+                            }
+                            break;
+                            
+                        case '\\':
+                            escaped=true;
+                            continue;
+                        default:
+                            continue;
+                    }
+                }
+                else
+                {
+                    // Handle name and value state machines
+                    if (invalue)
+                    {
+                        // parse the value
+                        switch (c)
+                        {
+                            case ' ':
+                            case '\t':
+                                continue;
+                                
+                            case '"':
+                                if (tokenstart<0)
+                                {
+                                    quoted=true;
+                                    tokenstart=i;
+                                }
+                                tokenend=i;
+                                if (i==last)
+                                {
+                                    value = hdr.substring(tokenstart, tokenend+1);
+                                    break;
+                                }
+                                continue;
+
+                            case ';':
+                            // case ',':
+                                if (tokenstart>=0)
+                                    value = hdr.substring(tokenstart, tokenend+1);
+                                else
+                                    value="";
+                                tokenstart = -1;
+                                invalue=false;
+                                break;
+                                
+                            default:
+                                if (tokenstart<0)
+                                    tokenstart=i;
+                                tokenend=i;
+                                if (i==last)
+                                {
+                                    value = hdr.substring(tokenstart, tokenend+1);
+                                    break;
+                                }
+                                continue;
+                        }
+                    }
+                    else
+                    {
+                        // parse the name
+                        switch (c)
+                        {
+                            case ' ':
+                            case '\t':
+                                continue;
+                                
+                            case '"':
+                                if (tokenstart<0)
+                                {
+                                    quoted=true;
+                                    tokenstart=i;
+                                }
+                                tokenend=i;
+                                if (i==last)
+                                {
+                                    name = hdr.substring(tokenstart, tokenend+1);
+                                    value = "";
+                                    break;
+                                }
+                                continue;
+
+                            case ';':
+                            // case ',':
+                                if (tokenstart>=0)
+                                {
+                                    name = hdr.substring(tokenstart, tokenend+1);
+                                    value = "";
+                                }
+                                tokenstart = -1;
+                                break;
+
+                            case '=':
+                                if (tokenstart>=0)
+                                    name = hdr.substring(tokenstart, tokenend+1);
+                                tokenstart = -1;
+                                invalue=true;
+                                continue;
+                                
+                            default:
+                                if (tokenstart<0)
+                                    tokenstart=i;
+                                tokenend=i;
+                                if (i==last)
+                                {
+                                    name = hdr.substring(tokenstart, tokenend+1);
+                                    value = "";
+                                    break;
+                                }
+                                continue;
+                        }
+                    }
+                }
+
+                // If after processing the current character we have a value and a name, then it is a cookie
+                if (value!=null && name!=null)
+                {
+                    // TODO handle unquoting during parsing!  But quoting is uncommon
+                    name=QuotedStringTokenizer.unquoteOnly(name);
+                    value=QuotedStringTokenizer.unquoteOnly(value);
+                    
+                    try
+                    {
+                        if (name.startsWith("$"))
+                        {
+                            String lowercaseName = name.toLowerCase(Locale.ENGLISH);
+                            if ("$path".equals(lowercaseName))
+                            {
+                                if (cookie!=null)
+                                    cookie.setPath(value);
+                            }
+                            else if ("$domain".equals(lowercaseName))
+                            {
+                                if (cookie!=null)
+                                    cookie.setDomain(value);
+                            }
+                            else if ("$port".equals(lowercaseName))
+                            {
+                                if (cookie!=null)
+                                    cookie.setComment("$port="+value);
+                            }
+                            else if ("$version".equals(lowercaseName))
+                            {
+                                version = Integer.parseInt(value);
+                            }
+                        }
+                        else
+                        {
+                            cookie = new Cookie(name, value);
+                            if (version > 0)
+                                cookie.setVersion(version);
+                            cookies = LazyList.add(cookies, cookie);
+                        }
+                    }
+                    catch (Exception e)
+                    {
+                        LOG.debug(e);
+                    }
+
+                    name = null;
+                    value = null;
+                }
+            }
+        }
+
+        _cookies = (Cookie[]) LazyList.toArray(cookies,Cookie.class);
+        _lastCookies=_cookies;
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/Dispatcher.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,550 @@
+//
+//  ========================================================================
+//  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.server;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.servlet.DispatcherType;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.util.Attributes;
+import org.eclipse.jetty.util.LazyList;
+import org.eclipse.jetty.util.MultiMap;
+import org.eclipse.jetty.util.UrlEncoded;
+
+/* ------------------------------------------------------------ */
+/** Servlet RequestDispatcher.
+ * 
+ * 
+ */
+public class Dispatcher implements RequestDispatcher
+{
+    /** Dispatch include attribute names */
+    public final static String __INCLUDE_PREFIX="javax.servlet.include.";
+
+    /** Dispatch include attribute names */
+    public final static String __FORWARD_PREFIX="javax.servlet.forward.";
+
+    /** JSP attributes */
+    public final static String __JSP_FILE="org.apache.catalina.jsp_file";
+
+    /* ------------------------------------------------------------ */
+    private final ContextHandler _contextHandler;
+    private final String _uri;
+    private final String _path;
+    private final String _dQuery;
+    private final String _named;
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @param contextHandler
+     * @param uri
+     * @param pathInContext
+     * @param query
+     */
+    public Dispatcher(ContextHandler contextHandler, String uri, String pathInContext, String query)
+    {
+        _contextHandler=contextHandler;
+        _uri=uri;
+        _path=pathInContext;
+        _dQuery=query;
+        _named=null;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /** Constructor. 
+     * @param contextHandler
+     * @param name
+     */
+    public Dispatcher(ContextHandler contextHandler,String name)
+        throws IllegalStateException
+    {
+        _contextHandler=contextHandler;
+        _named=name;
+        _uri=null;
+        _path=null;
+        _dQuery=null;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /* 
+     * @see javax.servlet.RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
+     */
+    public void forward(ServletRequest request, ServletResponse response) throws ServletException, IOException
+    {
+        forward(request, response, DispatcherType.FORWARD);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /* 
+     * @see javax.servlet.RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
+     */
+    public void error(ServletRequest request, ServletResponse response) throws ServletException, IOException
+    {
+        forward(request, response, DispatcherType.ERROR);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /* 
+     * @see javax.servlet.RequestDispatcher#include(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
+     */
+    public void include(ServletRequest request, ServletResponse response) throws ServletException, IOException
+    {
+        Request baseRequest=(request instanceof Request)?((Request)request):AbstractHttpConnection.getCurrentConnection().getRequest();
+      
+        
+        if (!(request instanceof HttpServletRequest))
+            request = new ServletRequestHttpWrapper(request);
+        if (!(response instanceof HttpServletResponse))
+            response = new ServletResponseHttpWrapper(response);
+            
+        
+        // TODO - allow stream or writer????
+        
+        final DispatcherType old_type = baseRequest.getDispatcherType();
+        final Attributes old_attr=baseRequest.getAttributes();
+        MultiMap old_params=baseRequest.getParameters();
+        try
+        {
+            baseRequest.setDispatcherType(DispatcherType.INCLUDE);
+            baseRequest.getConnection().include();
+            if (_named!=null)
+                _contextHandler.handle(_named,baseRequest, (HttpServletRequest)request, (HttpServletResponse)response);
+            else 
+            {
+                String query=_dQuery;
+                
+                if (query!=null)
+                {
+                    // force parameter extraction
+                    if (old_params==null)
+                    {
+                        baseRequest.extractParameters();
+                        old_params=baseRequest.getParameters();
+                    }
+                    
+                    MultiMap parameters=new MultiMap();
+                    UrlEncoded.decodeTo(query,parameters,baseRequest.getCharacterEncoding());
+                    
+                    if (old_params!=null && old_params.size()>0)
+                    {
+                        // Merge parameters.
+                        Iterator iter = old_params.entrySet().iterator();
+                        while (iter.hasNext())
+                        {
+                            Map.Entry entry = (Map.Entry)iter.next();
+                            String name=(String)entry.getKey();
+                            Object values=entry.getValue();
+                            for (int i=0;i<LazyList.size(values);i++)
+                                parameters.add(name, LazyList.get(values, i));
+                        }
+                    }
+                    baseRequest.setParameters(parameters);
+                }
+                
+                IncludeAttributes attr = new IncludeAttributes(old_attr); 
+                
+                attr._requestURI=_uri;
+                attr._contextPath=_contextHandler.getContextPath();
+                attr._servletPath=null; // set by ServletHandler
+                attr._pathInfo=_path;
+                attr._query=query;
+                
+                baseRequest.setAttributes(attr);
+                
+                _contextHandler.handle(_path,baseRequest, (HttpServletRequest)request, (HttpServletResponse)response);
+            }
+        }
+        finally
+        {
+            baseRequest.setAttributes(old_attr);
+            baseRequest.getConnection().included();
+            baseRequest.setParameters(old_params);
+            baseRequest.setDispatcherType(old_type);
+        }
+    }
+
+    
+    /* ------------------------------------------------------------ */
+    /* 
+     * @see javax.servlet.RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
+     */
+    protected void forward(ServletRequest request, ServletResponse response, DispatcherType dispatch) throws ServletException, IOException
+    {
+        Request baseRequest=(request instanceof Request)?((Request)request):AbstractHttpConnection.getCurrentConnection().getRequest();
+        Response base_response=baseRequest.getResponse();
+        response.resetBuffer();
+        base_response.fwdReset();
+       
+
+        if (!(request instanceof HttpServletRequest))
+            request = new ServletRequestHttpWrapper(request);
+        if (!(response instanceof HttpServletResponse))
+            response = new ServletResponseHttpWrapper(response);
+        
+        final boolean old_handled=baseRequest.isHandled();
+        final String old_uri=baseRequest.getRequestURI();
+        final String old_context_path=baseRequest.getContextPath();
+        final String old_servlet_path=baseRequest.getServletPath();
+        final String old_path_info=baseRequest.getPathInfo();
+        final String old_query=baseRequest.getQueryString();
+        final Attributes old_attr=baseRequest.getAttributes();
+        final DispatcherType old_type=baseRequest.getDispatcherType();
+        MultiMap<String> old_params=baseRequest.getParameters();
+        
+        try
+        {
+            baseRequest.setHandled(false);
+            baseRequest.setDispatcherType(dispatch);
+            
+            if (_named!=null)
+                _contextHandler.handle(_named,baseRequest, (HttpServletRequest)request, (HttpServletResponse)response);
+            else 
+            {
+                
+                // process any query string from the dispatch URL
+                String query=_dQuery;
+                if (query!=null)
+                {
+                    // force parameter extraction
+                    if (old_params==null)
+                    {
+                        baseRequest.extractParameters();
+                        old_params=baseRequest.getParameters();
+                    }
+                    
+                    baseRequest.mergeQueryString(query);
+                }
+                
+                ForwardAttributes attr = new ForwardAttributes(old_attr); 
+                
+                //If we have already been forwarded previously, then keep using the established 
+                //original value. Otherwise, this is the first forward and we need to establish the values.
+                //Note: the established value on the original request for pathInfo and
+                //for queryString is allowed to be null, but cannot be null for the other values.
+                if (old_attr.getAttribute(FORWARD_REQUEST_URI) != null)
+                {
+                    attr._pathInfo=(String)old_attr.getAttribute(FORWARD_PATH_INFO);
+                    attr._query=(String)old_attr.getAttribute(FORWARD_QUERY_STRING);
+                    attr._requestURI=(String)old_attr.getAttribute(FORWARD_REQUEST_URI);
+                    attr._contextPath=(String)old_attr.getAttribute(FORWARD_CONTEXT_PATH);
+                    attr._servletPath=(String)old_attr.getAttribute(FORWARD_SERVLET_PATH);
+                }
+                else
+                {
+                    attr._pathInfo=old_path_info;
+                    attr._query=old_query;
+                    attr._requestURI=old_uri;
+                    attr._contextPath=old_context_path;
+                    attr._servletPath=old_servlet_path;
+                }     
+                
+                baseRequest.setRequestURI(_uri);
+                baseRequest.setContextPath(_contextHandler.getContextPath());
+                baseRequest.setServletPath(null);
+                baseRequest.setPathInfo(_uri);
+                baseRequest.setAttributes(attr);
+                
+                _contextHandler.handle(_path,baseRequest, (HttpServletRequest)request, (HttpServletResponse)response);
+                
+                if (!baseRequest.getAsyncContinuation().isAsyncStarted())
+                    commitResponse(response,baseRequest);
+            }
+        }
+        finally
+        {
+            baseRequest.setHandled(old_handled);
+            baseRequest.setRequestURI(old_uri);
+            baseRequest.setContextPath(old_context_path);
+            baseRequest.setServletPath(old_servlet_path);
+            baseRequest.setPathInfo(old_path_info);
+            baseRequest.setAttributes(old_attr);
+            baseRequest.setParameters(old_params);
+            baseRequest.setQueryString(old_query);
+            baseRequest.setDispatcherType(old_type);
+        }
+    }
+
+
+    /* ------------------------------------------------------------ */
+    private void commitResponse(ServletResponse response, Request baseRequest) throws IOException
+    {
+        if (baseRequest.getResponse().isWriting())
+        {
+            try
+            {
+                response.getWriter().close();
+            }
+            catch (IllegalStateException e)
+            {
+                response.getOutputStream().close();
+            }
+        }
+        else
+        {
+            try
+            {
+                response.getOutputStream().close();
+            }
+            catch (IllegalStateException e)
+            {
+                response.getWriter().close();
+            }
+        }
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    private class ForwardAttributes implements Attributes
+    {
+        final Attributes _attr;
+        
+        String _requestURI;
+        String _contextPath;
+        String _servletPath;
+        String _pathInfo;
+        String _query;
+        
+        ForwardAttributes(Attributes attributes)
+        {
+            _attr=attributes;
+        }
+        
+        /* ------------------------------------------------------------ */
+        public Object getAttribute(String key)
+        {
+            if (Dispatcher.this._named==null)
+            {
+                if (key.equals(FORWARD_PATH_INFO))    
+                    return _pathInfo;
+                if (key.equals(FORWARD_REQUEST_URI))  
+                    return _requestURI;
+                if (key.equals(FORWARD_SERVLET_PATH)) 
+                    return _servletPath;
+                if (key.equals(FORWARD_CONTEXT_PATH)) 
+                    return _contextPath;
+                if (key.equals(FORWARD_QUERY_STRING)) 
+                    return _query;
+            }
+            
+            if (key.startsWith(__INCLUDE_PREFIX))
+                return null;
+            
+            return _attr.getAttribute(key);
+        }
+        
+        /* ------------------------------------------------------------ */
+        public Enumeration getAttributeNames()
+        {
+            HashSet set=new HashSet();
+            Enumeration e=_attr.getAttributeNames();
+            while(e.hasMoreElements())
+            {
+                String name=(String)e.nextElement();
+                if (!name.startsWith(__INCLUDE_PREFIX) &&
+                    !name.startsWith(__FORWARD_PREFIX))
+                    set.add(name);
+            }
+            
+            if (_named==null)
+            {
+                if (_pathInfo!=null)
+                    set.add(FORWARD_PATH_INFO);
+                else
+                    set.remove(FORWARD_PATH_INFO);
+                set.add(FORWARD_REQUEST_URI);
+                set.add(FORWARD_SERVLET_PATH);
+                set.add(FORWARD_CONTEXT_PATH);
+                if (_query!=null)
+                    set.add(FORWARD_QUERY_STRING);
+                else
+                    set.remove(FORWARD_QUERY_STRING);
+            }
+
+            return Collections.enumeration(set);
+        }
+        
+        /* ------------------------------------------------------------ */
+        public void setAttribute(String key, Object value)
+        {
+            if (_named==null && key.startsWith("javax.servlet."))
+            {
+                if (key.equals(FORWARD_PATH_INFO))         
+                    _pathInfo=(String)value;
+                else if (key.equals(FORWARD_REQUEST_URI))  
+                    _requestURI=(String)value;
+                else if (key.equals(FORWARD_SERVLET_PATH)) 
+                    _servletPath=(String)value;
+                else if (key.equals(FORWARD_CONTEXT_PATH)) 
+                    _contextPath=(String)value;
+                else if (key.equals(FORWARD_QUERY_STRING)) 
+                    _query=(String)value;
+                
+                else if (value==null)
+                    _attr.removeAttribute(key);
+                else
+                    _attr.setAttribute(key,value); 
+            }
+            else if (value==null)
+                _attr.removeAttribute(key);
+            else
+                _attr.setAttribute(key,value);
+        }
+        
+        /* ------------------------------------------------------------ */
+        @Override
+        public String toString() 
+        {
+            return "FORWARD+"+_attr.toString();
+        }
+
+        /* ------------------------------------------------------------ */
+        public void clearAttributes()
+        {
+            throw new IllegalStateException();
+        }
+
+        /* ------------------------------------------------------------ */
+        public void removeAttribute(String name)
+        {
+            setAttribute(name,null);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    private class IncludeAttributes implements Attributes
+    {
+        final Attributes _attr;
+        
+        String _requestURI;
+        String _contextPath;
+        String _servletPath;
+        String _pathInfo;
+        String _query;
+        
+        IncludeAttributes(Attributes attributes)
+        {
+            _attr=attributes;
+        }
+        
+        /* ------------------------------------------------------------ */
+        /* ------------------------------------------------------------ */
+        /* ------------------------------------------------------------ */
+        public Object getAttribute(String key)
+        {
+            if (Dispatcher.this._named==null)
+            {
+                if (key.equals(INCLUDE_PATH_INFO))    return _pathInfo;
+                if (key.equals(INCLUDE_SERVLET_PATH)) return _servletPath;
+                if (key.equals(INCLUDE_CONTEXT_PATH)) return _contextPath;
+                if (key.equals(INCLUDE_QUERY_STRING)) return _query;
+                if (key.equals(INCLUDE_REQUEST_URI))  return _requestURI;
+            }
+            else if (key.startsWith(__INCLUDE_PREFIX)) 
+                    return null;
+            
+            
+            return _attr.getAttribute(key);
+        }
+        
+        /* ------------------------------------------------------------ */
+        public Enumeration getAttributeNames()
+        {
+            HashSet set=new HashSet();
+            Enumeration e=_attr.getAttributeNames();
+            while(e.hasMoreElements())
+            {
+                String name=(String)e.nextElement();
+                if (!name.startsWith(__INCLUDE_PREFIX))
+                    set.add(name);
+            }
+            
+            if (_named==null)
+            {
+                if (_pathInfo!=null)
+                    set.add(INCLUDE_PATH_INFO);
+                else
+                    set.remove(INCLUDE_PATH_INFO);
+                set.add(INCLUDE_REQUEST_URI);
+                set.add(INCLUDE_SERVLET_PATH);
+                set.add(INCLUDE_CONTEXT_PATH);
+                if (_query!=null)
+                    set.add(INCLUDE_QUERY_STRING);
+                else
+                    set.remove(INCLUDE_QUERY_STRING);
+            }
+            
+            return Collections.enumeration(set);
+        }
+        
+        /* ------------------------------------------------------------ */
+        public void setAttribute(String key, Object value)
+        {
+            if (_named==null && key.startsWith("javax.servlet."))
+            {
+                if (key.equals(INCLUDE_PATH_INFO))         _pathInfo=(String)value;
+                else if (key.equals(INCLUDE_REQUEST_URI))  _requestURI=(String)value;
+                else if (key.equals(INCLUDE_SERVLET_PATH)) _servletPath=(String)value;
+                else if (key.equals(INCLUDE_CONTEXT_PATH)) _contextPath=(String)value;
+                else if (key.equals(INCLUDE_QUERY_STRING)) _query=(String)value;
+                else if (value==null)
+                    _attr.removeAttribute(key);
+                else
+                    _attr.setAttribute(key,value); 
+            }
+            else if (value==null)
+                _attr.removeAttribute(key);
+            else
+                _attr.setAttribute(key,value);
+        }
+        
+        /* ------------------------------------------------------------ */
+        @Override
+        public String toString() 
+        {
+            return "INCLUDE+"+_attr.toString();
+        }
+
+        /* ------------------------------------------------------------ */
+        public void clearAttributes()
+        {
+            throw new IllegalStateException();
+        }
+
+        /* ------------------------------------------------------------ */
+        public void removeAttribute(String name)
+        {
+            setAttribute(name,null);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/Handler.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,72 @@
+//
+//  ========================================================================
+//  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.server;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.server.handler.HandlerCollection;
+import org.eclipse.jetty.server.handler.HandlerWrapper;
+import org.eclipse.jetty.util.component.Destroyable;
+import org.eclipse.jetty.util.component.LifeCycle;
+
+/* ------------------------------------------------------------ */
+/** A Jetty Server Handler.
+ * 
+ * A Handler instance is required by a {@link Server} to handle incoming
+ * HTTP requests.  A Handler may: <ul>
+ * <li>Completely generate the HTTP Response</li>
+ * <li>Examine/modify the request and call another Handler (see {@link HandlerWrapper}).
+ * <li>Pass the request to one or more other Handlers (see {@link HandlerCollection}).
+ * </ul>
+ * 
+ * Handlers are passed the servlet API request and response object, but are 
+ * not Servlets.  The servlet container is implemented by handlers for 
+ * context, security, session and servlet that modify the request object 
+ * before passing it to the next stage of handling.
+ * 
+ */
+public interface Handler extends LifeCycle, Destroyable
+{
+    /* ------------------------------------------------------------ */
+    /** Handle a request.
+     * @param target The target of the request - either a URI or a name.
+     * @param baseRequest The original unwrapped request object.
+     * @param request The request either as the {@link Request}
+     * object or a wrapper of that request. The {@link AbstractHttpConnection#getCurrentConnection()} 
+     * method can be used access the Request object if required.
+     * @param response The response as the {@link Response}
+     * object or a wrapper of that request. The {@link AbstractHttpConnection#getCurrentConnection()} 
+     * method can be used access the Response object if required.
+     * @throws IOException
+     * @throws ServletException
+     */
+    public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
+        throws IOException, ServletException;
+    
+    public void setServer(Server server);
+    public Server getServer();
+    
+    public void destroy();
+    
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/HandlerContainer.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,57 @@
+//
+//  ========================================================================
+//  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.server;
+
+import org.eclipse.jetty.util.component.LifeCycle;
+
+/**
+ * A Handler that contains other Handlers.
+ * <p>
+ * The contained handlers may be one (see @{link {@link org.eclipse.jetty.server.handler.HandlerWrapper})
+ * or many (see {@link org.eclipse.jetty.server.handler.HandlerList} or {@link org.eclipse.jetty.server.handler.HandlerCollection}. 
+ *
+ */
+public interface HandlerContainer extends LifeCycle
+{
+    /* ------------------------------------------------------------ */
+    /**
+     * @return array of handlers directly contained by this handler.
+     */
+    public Handler[] getHandlers();
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @return array of all handlers contained by this handler and it's children
+     */
+    public Handler[] getChildHandlers();
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @param byclass
+     * @return array of all handlers contained by this handler and it's children of the passed type.
+     */
+    public Handler[] getChildHandlersByClass(Class<?> byclass);
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @param byclass
+     * @return first handler of all handlers contained by this handler and it's children of the passed type.
+     */
+    public <T extends Handler> T getChildHandlerByClass(Class<T> byclass);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/HttpInput.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,75 @@
+//
+//  ========================================================================
+//  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.server;
+
+import java.io.IOException;
+
+import javax.servlet.ServletInputStream;
+
+import org.eclipse.jetty.http.HttpParser;
+import org.eclipse.jetty.io.Buffer;
+import org.eclipse.jetty.io.EofException;
+
+public class HttpInput extends ServletInputStream
+{
+    protected final AbstractHttpConnection _connection;
+    protected final HttpParser _parser;
+
+    /* ------------------------------------------------------------ */
+    public HttpInput(AbstractHttpConnection connection)
+    {
+        _connection=connection;
+        _parser=(HttpParser)connection.getParser();
+    }
+    
+    /* ------------------------------------------------------------ */
+    /*
+     * @see java.io.InputStream#read()
+     */
+    @Override
+    public int read() throws IOException
+    {
+        byte[] bytes = new byte[1];
+        int read = read(bytes, 0, 1);
+        return read < 0 ? -1 : 0xff & bytes[0];
+    }
+    
+    /* ------------------------------------------------------------ */
+    /* 
+     * @see java.io.InputStream#read(byte[], int, int)
+     */
+    @Override
+    public int read(byte[] b, int off, int len) throws IOException
+    {
+        int l=-1;
+        Buffer content=_parser.blockForContent(_connection.getMaxIdleTime());
+        if (content!=null)
+            l= content.get(b, off, len);
+        else if (_connection.isEarlyEOF())
+            throw new EofException("early EOF");
+        return l;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public int available() throws IOException
+    {
+        return _parser.available();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/HttpOutput.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,183 @@
+//
+//  ========================================================================
+//  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.server;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import javax.servlet.ServletOutputStream;
+
+import org.eclipse.jetty.http.AbstractGenerator;
+import org.eclipse.jetty.http.Generator;
+import org.eclipse.jetty.io.Buffer;
+import org.eclipse.jetty.io.ByteArrayBuffer;
+import org.eclipse.jetty.io.EofException;
+import org.eclipse.jetty.util.ByteArrayOutputStream2;
+
+/** Output.
+ * 
+ * <p>
+ * Implements  {@link javax.servlet.ServletOutputStream} from the <code>javax.servlet</code> package.   
+ * </p>
+ * A {@link ServletOutputStream} implementation that writes content
+ * to a {@link AbstractGenerator}.   The class is designed to be reused
+ * and can be reopened after a close.
+ */
+public class HttpOutput extends ServletOutputStream 
+{
+    protected final AbstractHttpConnection _connection;
+    protected final AbstractGenerator _generator;
+    private boolean _closed;
+    private ByteArrayBuffer _onebyte;
+    
+    // These are held here for reuse by Writer
+    String _characterEncoding;
+    Writer _converter;
+    char[] _chars;
+    ByteArrayOutputStream2 _bytes;
+
+    /* ------------------------------------------------------------ */
+    public HttpOutput(AbstractHttpConnection connection)
+    {
+        _connection=connection;
+        _generator=(AbstractGenerator)connection.getGenerator();
+    }
+
+    /* ------------------------------------------------------------ */
+    public int getMaxIdleTime()
+    {
+        return _connection.getMaxIdleTime();
+    }
+    
+    /* ------------------------------------------------------------ */
+    public boolean isWritten()
+    {
+        return _generator.getContentWritten()>0;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /*
+     * @see java.io.OutputStream#close()
+     */
+    @Override
+    public void close() throws IOException
+    {
+        _closed=true;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public boolean isClosed()
+    {
+        return _closed;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public void reopen()
+    {
+        _closed=false;
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public void flush() throws IOException
+    {
+        _generator.flush(getMaxIdleTime());
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void write(byte[] b, int off, int len) throws IOException
+    {
+        write(new ByteArrayBuffer(b,off,len));
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see java.io.OutputStream#write(byte[])
+     */
+    @Override
+    public void write(byte[] b) throws IOException
+    {
+        write(new ByteArrayBuffer(b));
+    }
+
+    
+    /* ------------------------------------------------------------ */
+    /*
+     * @see java.io.OutputStream#write(int)
+     */
+    @Override
+    public void write(int b) throws IOException
+    {
+        if (_onebyte==null)
+            _onebyte=new ByteArrayBuffer(1);
+        else
+            _onebyte.clear();
+        _onebyte.put((byte)b);
+        write(_onebyte);
+    }
+
+    /* ------------------------------------------------------------ */
+    private void write(Buffer buffer) throws IOException
+    {
+        if (_closed)
+            throw new IOException("Closed");
+        if (!_generator.isOpen())
+            throw new EofException();
+        
+        // Block until we can add _content.
+        while (_generator.isBufferFull())
+        {
+            _generator.blockForOutput(getMaxIdleTime());
+            if (_closed)
+                throw new IOException("Closed");
+            if (!_generator.isOpen())
+                throw new EofException();
+        }
+
+        // Add the _content
+        _generator.addContent(buffer, Generator.MORE);
+
+        // Have to flush and complete headers?
+        
+        if (_generator.isAllContentWritten())
+        {
+            flush();
+            close();
+        } 
+        else if (_generator.isBufferFull())
+            _connection.commitResponse(Generator.MORE);
+
+        // Block until our buffer is free
+        while (buffer.length() > 0 && _generator.isOpen())
+        {
+            _generator.blockForOutput(getMaxIdleTime());
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /* 
+     * @see javax.servlet.ServletOutputStream#print(java.lang.String)
+     */
+    @Override
+    public void print(String s) throws IOException
+    {
+        write(s.getBytes());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/HttpWriter.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,302 @@
+//
+//  ========================================================================
+//  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.server;
+
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+import org.eclipse.jetty.http.AbstractGenerator;
+import org.eclipse.jetty.util.ByteArrayOutputStream2;
+import org.eclipse.jetty.util.StringUtil;
+
+/** OutputWriter.
+ * A writer that can wrap a {@link HttpOutput} stream and provide
+ * character encodings.
+ *
+ * The UTF-8 encoding is done by this class and no additional 
+ * buffers or Writers are used.
+ * The UTF-8 code was inspired by http://javolution.org
+ */
+public class HttpWriter extends Writer
+{
+    public static final int MAX_OUTPUT_CHARS = 512; 
+    
+    private static final int WRITE_CONV = 0;
+    private static final int WRITE_ISO1 = 1;
+    private static final int WRITE_UTF8 = 2;
+    
+    final HttpOutput _out;
+    final AbstractGenerator _generator;
+    int _writeMode;
+    int _surrogate;
+
+    /* ------------------------------------------------------------ */
+    public HttpWriter(HttpOutput out)
+    {
+        _out=out;
+        _generator=_out._generator;
+        _surrogate=0; // AS lastUTF16CodePoint
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setCharacterEncoding(String encoding)
+    {
+        if (encoding == null || StringUtil.__ISO_8859_1.equalsIgnoreCase(encoding))
+        {
+            _writeMode = WRITE_ISO1;
+        }
+        else if (StringUtil.__UTF8.equalsIgnoreCase(encoding))
+        {
+            _writeMode = WRITE_UTF8;
+        }
+        else
+        {
+            _writeMode = WRITE_CONV;
+            if (_out._characterEncoding == null || !_out._characterEncoding.equalsIgnoreCase(encoding))
+                _out._converter = null; // Set lazily in getConverter()
+        }
+        
+        _out._characterEncoding = encoding;
+        if (_out._bytes==null)
+            _out._bytes = new ByteArrayOutputStream2(MAX_OUTPUT_CHARS);
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void close() throws IOException
+    {
+        _out.close();
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void flush() throws IOException
+    {
+        _out.flush();
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void write (String s,int offset, int length) throws IOException
+    {   
+        while (length > MAX_OUTPUT_CHARS)
+        {
+            write(s, offset, MAX_OUTPUT_CHARS);
+            offset += MAX_OUTPUT_CHARS;
+            length -= MAX_OUTPUT_CHARS;
+        }
+
+        if (_out._chars==null)
+        {
+            _out._chars = new char[MAX_OUTPUT_CHARS]; 
+        }
+        char[] chars = _out._chars;
+        s.getChars(offset, offset + length, chars, 0);
+        write(chars, 0, length);
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void write (char[] s,int offset, int length) throws IOException
+    {              
+        HttpOutput out = _out; 
+        
+        while (length > 0)
+        {  
+            out._bytes.reset();
+            int chars = length>MAX_OUTPUT_CHARS?MAX_OUTPUT_CHARS:length;
+            
+            switch (_writeMode)
+            {
+                case WRITE_CONV:
+                {
+                    Writer converter=getConverter();
+                    converter.write(s, offset, chars);
+                    converter.flush();
+                }
+                break;
+
+                case WRITE_ISO1:
+                {
+                    byte[] buffer=out._bytes.getBuf();
+                    int bytes=out._bytes.getCount();
+                    
+                    if (chars>buffer.length-bytes)
+                        chars=buffer.length-bytes;
+
+                    for (int i = 0; i < chars; i++)
+                    {
+                        int c = s[offset+i];
+                        buffer[bytes++]=(byte)(c<256?c:'?'); // ISO-1 and UTF-8 match for 0 - 255
+                    }
+                    if (bytes>=0)
+                        out._bytes.setCount(bytes);
+
+                    break;
+                }
+
+                case WRITE_UTF8:
+                {
+                    byte[] buffer=out._bytes.getBuf();
+                    int bytes=out._bytes.getCount();
+
+                    if (bytes+chars>buffer.length)
+                        chars=buffer.length-bytes;
+
+                    for (int i = 0; i < chars; i++)
+                    {
+                        int code = s[offset+i];
+
+                        // Do we already have a surrogate?
+                        if(_surrogate==0)
+                        {
+                            // No - is this char code a surrogate?
+                            if(Character.isHighSurrogate((char)code))
+                            {
+                                _surrogate=code; // UCS-?
+                                continue;
+                            }                            
+                        }
+                        // else handle a low surrogate
+                        else if(Character.isLowSurrogate((char)code))
+                        {
+                            code = Character.toCodePoint((char)_surrogate, (char)code); // UCS-4
+                        }
+                        // else UCS-2
+                        else
+                        {
+                            code=_surrogate; // UCS-2
+                            _surrogate=0; // USED
+                            i--;
+                        }
+
+                        if ((code & 0xffffff80) == 0) 
+                        {
+                            // 1b
+                            if (bytes>=buffer.length)
+                            {
+                                chars=i;
+                                break;
+                            }
+                            buffer[bytes++]=(byte)(code);
+                        }
+                        else
+                        {
+                            if((code&0xfffff800)==0)
+                            {
+                                // 2b
+                                if (bytes+2>buffer.length)
+                                {
+                                    chars=i;
+                                    break;
+                                }
+                                buffer[bytes++]=(byte)(0xc0|(code>>6));
+                                buffer[bytes++]=(byte)(0x80|(code&0x3f));
+                            }
+                            else if((code&0xffff0000)==0)
+                            {
+                                // 3b
+                                if (bytes+3>buffer.length)
+                                {
+                                    chars=i;
+                                    break;
+                                }
+                                buffer[bytes++]=(byte)(0xe0|(code>>12));
+                                buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
+                                buffer[bytes++]=(byte)(0x80|(code&0x3f));
+                            }
+                            else if((code&0xff200000)==0)
+                            {
+                                // 4b
+                                if (bytes+4>buffer.length)
+                                {
+                                    chars=i;
+                                    break;
+                                }
+                                buffer[bytes++]=(byte)(0xf0|(code>>18));
+                                buffer[bytes++]=(byte)(0x80|((code>>12)&0x3f));
+                                buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
+                                buffer[bytes++]=(byte)(0x80|(code&0x3f));
+                            }
+                            else if((code&0xf4000000)==0)
+                            {
+                                // 5b
+                                if (bytes+5>buffer.length)
+                                {
+                                    chars=i;
+                                    break;
+                                }
+                                buffer[bytes++]=(byte)(0xf8|(code>>24));
+                                buffer[bytes++]=(byte)(0x80|((code>>18)&0x3f));
+                                buffer[bytes++]=(byte)(0x80|((code>>12)&0x3f));
+                                buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
+                                buffer[bytes++]=(byte)(0x80|(code&0x3f));
+                            }
+                            else if((code&0x80000000)==0)
+                            {
+                                // 6b
+                                if (bytes+6>buffer.length)
+                                {
+                                    chars=i;
+                                    break;
+                                }
+                                buffer[bytes++]=(byte)(0xfc|(code>>30));
+                                buffer[bytes++]=(byte)(0x80|((code>>24)&0x3f));
+                                buffer[bytes++]=(byte)(0x80|((code>>18)&0x3f));
+                                buffer[bytes++]=(byte)(0x80|((code>>12)&0x3f));
+                                buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
+                                buffer[bytes++]=(byte)(0x80|(code&0x3f));
+                            }
+                            else
+                            {
+                                buffer[bytes++]=(byte)('?');
+                            } 
+
+                            _surrogate=0; // USED
+
+                            if (bytes==buffer.length)
+                            {
+                                chars=i+1;
+                                break;
+                            }
+                        }
+                    }
+                    out._bytes.setCount(bytes);
+                    break;
+                }
+                default:
+                    throw new IllegalStateException();
+            }
+            
+            out._bytes.writeTo(out);
+            length-=chars;
+            offset+=chars;
+        }
+    }
+    
+    
+    /* ------------------------------------------------------------ */
+    private Writer getConverter() throws IOException
+    {
+        if (_out._converter == null)
+            _out._converter = new OutputStreamWriter(_out._bytes, _out._characterEncoding);
+        return _out._converter;
+    }   
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/InclusiveByteRange.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,227 @@
+//
+//  ========================================================================
+//  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.server;
+
+import java.util.Enumeration;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.eclipse.jetty.util.LazyList;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/* ------------------------------------------------------------ */
+/** Byte range inclusive of end points.
+ * <PRE>
+ * 
+ *   parses the following types of byte ranges:
+ * 
+ *       bytes=100-499
+ *       bytes=-300
+ *       bytes=100-
+ *       bytes=1-2,2-3,6-,-2
+ *
+ *   given an entity length, converts range to string
+ * 
+ *       bytes 100-499/500
+ * 
+ * </PRE>
+ * 
+ * Based on RFC2616 3.12, 14.16, 14.35.1, 14.35.2
+ * @version $version$
+ * 
+ */
+public class InclusiveByteRange 
+{
+    private static final Logger LOG = Log.getLogger(InclusiveByteRange.class);
+
+    long first = 0;
+    long last  = 0;    
+
+    public InclusiveByteRange(long first, long last)
+    {
+        this.first = first;
+        this.last = last;
+    }
+    
+    public long getFirst()
+    {
+        return first;
+    }
+
+    public long getLast()
+    {
+        return last;
+    }    
+
+
+    
+    /* ------------------------------------------------------------ */
+    /** 
+     * @param headers Enumeration of Range header fields.
+     * @param size Size of the resource.
+     * @return LazyList of satisfiable ranges
+     */
+    public static List satisfiableRanges(Enumeration headers, long size)
+    {
+        Object satRanges=null;
+        
+        // walk through all Range headers
+    headers:
+        while (headers.hasMoreElements())
+        {
+            String header = (String) headers.nextElement();
+            StringTokenizer tok = new StringTokenizer(header,"=,",false);
+            String t=null;
+            try
+            {
+                // read all byte ranges for this header 
+                while (tok.hasMoreTokens())
+                {
+                    try
+                    {
+                        t = tok.nextToken().trim();
+
+                        long first = -1;
+                        long last = -1;
+                        int d = t.indexOf('-');
+                        if (d < 0 || t.indexOf("-",d + 1) >= 0)
+                        {
+                            if ("bytes".equals(t))
+                                continue;
+                            LOG.warn("Bad range format: {}",t);
+                            continue headers;
+                        }
+                        else if (d == 0)
+                        {
+                            if (d + 1 < t.length())
+                                last = Long.parseLong(t.substring(d + 1).trim());
+                            else
+                            {
+                                LOG.warn("Bad range format: {}",t);
+                                continue;
+                            }
+                        }
+                        else if (d + 1 < t.length())
+                        {
+                            first = Long.parseLong(t.substring(0,d).trim());
+                            last = Long.parseLong(t.substring(d + 1).trim());
+                        }
+                        else
+                            first = Long.parseLong(t.substring(0,d).trim());
+
+                        if (first == -1 && last == -1)
+                            continue headers;
+
+                        if (first != -1 && last != -1 && (first > last))
+                            continue headers;
+
+                        if (first < size)
+                        {
+                            InclusiveByteRange range = new InclusiveByteRange(first,last);
+                            satRanges = LazyList.add(satRanges,range);
+                        }
+                    }
+                    catch (NumberFormatException e)
+                    {
+                        LOG.warn("Bad range format: {}",t);
+                        LOG.ignore(e);
+                        continue;
+                    }
+                }
+            }
+            catch(Exception e)
+            {
+                LOG.warn("Bad range format: {}",t);
+                LOG.ignore(e);
+            }    
+        }
+        return LazyList.getList(satRanges,true);
+    }
+
+    /* ------------------------------------------------------------ */
+    public long getFirst(long size)
+    {
+        if (first<0)
+        {
+            long tf=size-last;
+            if (tf<0)
+                tf=0;
+            return tf;
+        }
+        return first;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public long getLast(long size)
+    {
+        if (first<0)
+            return size-1;
+        
+        if (last<0 ||last>=size)
+            return size-1;
+        return last;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public long getSize(long size)
+    {
+        return getLast(size)-getFirst(size)+1;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    public String toHeaderRangeString(long size)
+    {
+        StringBuilder sb = new StringBuilder(40);
+        sb.append("bytes ");
+        sb.append(getFirst(size));
+        sb.append('-');
+        sb.append(getLast(size));
+        sb.append("/");
+        sb.append(size);
+        return sb.toString();
+    }
+
+    /* ------------------------------------------------------------ */
+    public static String to416HeaderRangeString(long size)
+    {
+        StringBuilder sb = new StringBuilder(40);
+        sb.append("bytes */");
+        sb.append(size);
+        return sb.toString();
+    }
+
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public String toString()
+    {
+        StringBuilder sb = new StringBuilder(60);
+        sb.append(Long.toString(first));
+        sb.append(":");
+        sb.append(Long.toString(last));
+        return sb.toString();
+    }
+
+
+}
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/LocalConnector.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,176 @@
+//
+//  ========================================================================
+//  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.server;
+
+import java.io.IOException;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.io.ByteArrayBuffer;
+import org.eclipse.jetty.io.ByteArrayEndPoint;
+import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+public class LocalConnector extends AbstractConnector
+{
+    private static final Logger LOG = Log.getLogger(LocalConnector.class);
+    private final BlockingQueue<Request> _requests = new LinkedBlockingQueue<Request>();
+    
+    public LocalConnector()
+    {
+        setMaxIdleTime(30000);
+    }
+
+    public Object getConnection()
+    {
+        return this;
+    }
+
+    public String getResponses(String requests) throws Exception
+    {
+        return getResponses(requests, false);
+    }
+
+    public String getResponses(String requests, boolean keepOpen) throws Exception
+    {
+        ByteArrayBuffer result = getResponses(new ByteArrayBuffer(requests, StringUtil.__ISO_8859_1), keepOpen);
+        return result==null?null:result.toString(StringUtil.__ISO_8859_1);
+    }
+
+    public ByteArrayBuffer getResponses(ByteArrayBuffer requestsBuffer, boolean keepOpen) throws Exception
+    {
+        CountDownLatch latch = new CountDownLatch(1);
+        Request request = new Request(requestsBuffer, keepOpen, latch);
+        _requests.add(request);
+        latch.await(getMaxIdleTime(),TimeUnit.MILLISECONDS);
+        return request.getResponsesBuffer();
+    }
+
+    @Override
+    protected void accept(int acceptorID) throws IOException, InterruptedException
+    {
+        Request request = _requests.take();
+        getThreadPool().dispatch(request);
+    }
+
+    public void open() throws IOException
+    {
+    }
+
+    public void close() throws IOException
+    {
+    }
+
+    public int getLocalPort()
+    {
+        return -1;
+    }
+
+    public void executeRequest(String rawRequest) throws IOException
+    {
+        Request request = new Request(new ByteArrayBuffer(rawRequest, "UTF-8"), true, null);
+        _requests.add(request);
+    }
+
+    private class Request implements Runnable
+    {
+        private final ByteArrayBuffer _requestsBuffer;
+        private final boolean _keepOpen;
+        private final CountDownLatch _latch;
+        private volatile ByteArrayBuffer _responsesBuffer;
+
+        private Request(ByteArrayBuffer requestsBuffer, boolean keepOpen, CountDownLatch latch)
+        {
+            _requestsBuffer = requestsBuffer;
+            _keepOpen = keepOpen;
+            _latch = latch;
+        }
+
+        public void run()
+        {
+            try
+            {
+                ByteArrayEndPoint endPoint = new ByteArrayEndPoint(_requestsBuffer.asArray(), 1024)
+                {
+                    @Override
+                    public void setConnection(Connection connection)
+                    {
+                        if (getConnection()!=null && connection!=getConnection())
+                            connectionUpgraded(getConnection(),connection);
+                        super.setConnection(connection);
+                    }
+                };
+
+                endPoint.setGrowOutput(true);
+                AbstractHttpConnection connection = new BlockingHttpConnection(LocalConnector.this, endPoint, getServer());
+                endPoint.setConnection(connection);
+                connectionOpened(connection);
+
+                boolean leaveOpen = _keepOpen;
+                try
+                {
+                    while (endPoint.getIn().length() > 0 && endPoint.isOpen())
+                    {
+                        while (true)
+                        {
+                            final Connection con = endPoint.getConnection();
+                            final Connection next = con.handle();
+                            if (next!=con)
+                            {  
+                                endPoint.setConnection(next);
+                                continue;
+                            }
+                            break;
+                        }
+                    }
+                }
+                catch (IOException x)
+                {
+                    LOG.debug(x);
+                    leaveOpen = false;
+                }
+                catch (Exception x)
+                {
+                    LOG.warn(x);
+                    leaveOpen = false;
+                }
+                finally
+                {
+                    if (!leaveOpen)
+                        connectionClosed(connection);
+                    _responsesBuffer = endPoint.getOut();
+                }
+            }
+            finally
+            {
+                if (_latch != null)
+                    _latch.countDown();
+            }
+        }
+
+        public ByteArrayBuffer getResponsesBuffer()
+        {
+            return _responsesBuffer;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/NCSARequestLog.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,726 @@
+//
+//  ========================================================================
+//  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.server;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import javax.servlet.http.Cookie;
+
+import org.eclipse.jetty.http.HttpHeaders;
+import org.eclipse.jetty.http.PathMap;
+import org.eclipse.jetty.util.DateCache;
+import org.eclipse.jetty.util.RolloverFileOutputStream;
+import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.component.AbstractLifeCycle;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * This {@link RequestLog} implementation outputs logs in the pseudo-standard
+ * NCSA common log format. Configuration options allow a choice between the
+ * standard Common Log Format (as used in the 3 log format) and the Combined Log
+ * Format (single log format). This log format can be output by most web
+ * servers, and almost all web log analysis software can understand these
+ * formats.
+ *
+ * @org.apache.xbean.XBean element="ncsaLog"
+ */
+
+/* ------------------------------------------------------------ */
+/**
+ */
+public class NCSARequestLog extends AbstractLifeCycle implements RequestLog
+{
+    private static final Logger LOG = Log.getLogger(NCSARequestLog.class);
+    private static ThreadLocal<StringBuilder> _buffers = new ThreadLocal<StringBuilder>()
+            {
+                @Override
+                protected StringBuilder initialValue()
+                {
+                    return new StringBuilder(256);
+                }
+            };
+
+    private String _filename;
+    private boolean _extended;
+    private boolean _append;
+    private int _retainDays;
+    private boolean _closeOut;
+    private boolean _preferProxiedForAddress;
+    private String _logDateFormat = "dd/MMM/yyyy:HH:mm:ss Z";
+    private String _filenameDateFormat = null;
+    private Locale _logLocale = Locale.getDefault();
+    private String _logTimeZone = "GMT";
+    private String[] _ignorePaths;
+    private boolean _logLatency = false;
+    private boolean _logCookies = false;
+    private boolean _logServer = false;
+    private boolean _logDispatch = false;
+
+    private transient OutputStream _out;
+    private transient OutputStream _fileOut;
+    private transient DateCache _logDateCache;
+    private transient PathMap _ignorePathMap;
+    private transient Writer _writer;
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Create request log object with default settings.
+     */
+    public NCSARequestLog()
+    {
+        _extended = true;
+        _append = true;
+        _retainDays = 31;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Create request log object with specified output file name.
+     * 
+     * @param filename the file name for the request log.
+     *                 This may be in the format expected
+     *                 by {@link RolloverFileOutputStream}
+     */
+    public NCSARequestLog(String filename)
+    {
+        _extended = true;
+        _append = true;
+        _retainDays = 31;
+        setFilename(filename);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set the output file name of the request log.
+     * The file name may be in the format expected by
+     * {@link RolloverFileOutputStream}.
+     * 
+     * @param filename file name of the request log
+     *                
+     */
+    public void setFilename(String filename)
+    {
+        if (filename != null)
+        {
+            filename = filename.trim();
+            if (filename.length() == 0)
+                filename = null;
+        }
+        _filename = filename;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Retrieve the output file name of the request log.
+     * 
+     * @return file name of the request log
+     */
+    public String getFilename()
+    {
+        return _filename;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Retrieve the file name of the request log with the expanded
+     * date wildcard if the output is written to the disk using
+     * {@link RolloverFileOutputStream}.
+     * 
+     * @return file name of the request log, or null if not applicable
+     */
+    public String getDatedFilename()
+    {
+        if (_fileOut instanceof RolloverFileOutputStream)
+            return ((RolloverFileOutputStream)_fileOut).getDatedFilename();
+        return null;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set the timestamp format for request log entries in the file.
+     * If this is not set, the pre-formated request timestamp is used.
+     * 
+     * @param format timestamp format string 
+     */
+    public void setLogDateFormat(String format)
+    {
+        _logDateFormat = format;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Retrieve the timestamp format string for request log entries.
+     * 
+     * @return timestamp format string.
+     */
+    public String getLogDateFormat()
+    {
+        return _logDateFormat;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set the locale of the request log.
+     * 
+     * @param logLocale locale object
+     */
+    public void setLogLocale(Locale logLocale)
+    {
+        _logLocale = logLocale;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Retrieve the locale of the request log.
+     * 
+     * @return locale object
+     */
+    public Locale getLogLocale()
+    {
+        return _logLocale;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set the timezone of the request log.
+     * 
+     * @param tz timezone string
+     */
+    public void setLogTimeZone(String tz)
+    {
+        _logTimeZone = tz;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Retrieve the timezone of the request log.
+     * 
+     * @return timezone string
+     */
+    public String getLogTimeZone()
+    {
+        return _logTimeZone;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set the number of days before rotated log files are deleted.
+     * 
+     * @param retainDays number of days to keep a log file
+     */
+    public void setRetainDays(int retainDays)
+    {
+        _retainDays = retainDays;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Retrieve the number of days before rotated log files are deleted.
+     * 
+     * @return number of days to keep a log file
+     */
+    public int getRetainDays()
+    {
+        return _retainDays;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set the extended request log format flag.
+     * 
+     * @param extended true - log the extended request information,
+     *                 false - do not log the extended request information
+     */
+    public void setExtended(boolean extended)
+    {
+        _extended = extended;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Retrieve the extended request log format flag.
+     * 
+     * @return value of the flag
+     */
+    public boolean isExtended()
+    {
+        return _extended;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set append to log flag.
+     * 
+     * @param append true - request log file will be appended after restart,
+     *               false - request log file will be overwritten after restart
+     */
+    public void setAppend(boolean append)
+    {
+        _append = append;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Retrieve append to log flag.
+     * 
+     * @return value of the flag
+     */
+    public boolean isAppend()
+    {
+        return _append;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set request paths that will not be logged.
+     * 
+     * @param ignorePaths array of request paths
+     */
+    public void setIgnorePaths(String[] ignorePaths)
+    {
+        _ignorePaths = ignorePaths;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Retrieve the request paths that will not be logged.
+     * 
+     * @return array of request paths
+     */
+    public String[] getIgnorePaths()
+    {
+        return _ignorePaths;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Controls logging of the request cookies.
+     * 
+     * @param logCookies true - values of request cookies will be logged,
+     *                   false - values of request cookies will not be logged
+     */
+    public void setLogCookies(boolean logCookies)
+    {
+        _logCookies = logCookies;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Retrieve log cookies flag
+     * 
+     * @return value of the flag
+     */
+    public boolean getLogCookies()
+    {
+        return _logCookies;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Controls logging of the request hostname.
+     * 
+     * @param logServer true - request hostname will be logged,
+     *                  false - request hostname will not be logged
+     */
+    public void setLogServer(boolean logServer)
+    {
+        _logServer = logServer;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Retrieve log hostname flag.
+     * 
+     * @return value of the flag
+     */
+    public boolean getLogServer()
+    {
+        return _logServer;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Controls logging of request processing time.
+     * 
+     * @param logLatency true - request processing time will be logged
+     *                   false - request processing time will not be logged
+     */
+    public void setLogLatency(boolean logLatency)
+    {
+        _logLatency = logLatency;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Retrieve log request processing time flag.
+     * 
+     * @return value of the flag
+     */
+    public boolean getLogLatency()
+    {
+        return _logLatency;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Controls whether the actual IP address of the connection or
+     * the IP address from the X-Forwarded-For header will be logged.
+     * 
+     * @param preferProxiedForAddress true - IP address from header will be logged,
+     *                                false - IP address from the connection will be logged
+     */
+    public void setPreferProxiedForAddress(boolean preferProxiedForAddress)
+    {
+        _preferProxiedForAddress = preferProxiedForAddress;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Retrieved log X-Forwarded-For IP address flag.
+     * 
+     * @return value of the flag
+     */
+    public boolean getPreferProxiedForAddress()
+    {
+        return _preferProxiedForAddress;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set the log file name date format.
+     * @see RolloverFileOutputStream#RolloverFileOutputStream(String, boolean, int, TimeZone, String, String)
+     * 
+     * @param logFileDateFormat format string that is passed to {@link RolloverFileOutputStream}
+     */
+    public void setFilenameDateFormat(String logFileDateFormat)
+    {
+        _filenameDateFormat = logFileDateFormat;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Retrieve the file name date format string.
+     * 
+     * @return the log File Date Format
+     */
+    public String getFilenameDateFormat()
+    {
+        return _filenameDateFormat;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** 
+     * Controls logging of the request dispatch time
+     * 
+     * @param value true - request dispatch time will be logged
+     *              false - request dispatch time will not be logged
+     */
+    public void setLogDispatch(boolean value)
+    {
+        _logDispatch = value;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Retrieve request dispatch time logging flag
+     * 
+     * @return value of the flag
+     */
+    public boolean isLogDispatch()
+    {
+        return _logDispatch;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Writes the request and response information to the output stream.
+     * 
+     * @see org.eclipse.jetty.server.RequestLog#log(org.eclipse.jetty.server.Request, org.eclipse.jetty.server.Response)
+     */
+    public void log(Request request, Response response)
+    {
+        try
+        {
+            if (_ignorePathMap != null && _ignorePathMap.getMatch(request.getRequestURI()) != null)
+                return;
+
+            if (_fileOut == null)
+                return;
+
+            StringBuilder buf= _buffers.get();
+            buf.setLength(0);
+
+            if (_logServer)
+            {
+                buf.append(request.getServerName());
+                buf.append(' ');
+            }
+
+            String addr = null;
+            if (_preferProxiedForAddress)
+            {
+                addr = request.getHeader(HttpHeaders.X_FORWARDED_FOR);
+            }
+
+            if (addr == null)
+                addr = request.getRemoteAddr();
+
+            buf.append(addr);
+            buf.append(" - ");
+            Authentication authentication=request.getAuthentication();
+            if (authentication instanceof Authentication.User)
+                buf.append(((Authentication.User)authentication).getUserIdentity().getUserPrincipal().getName());
+            else
+                buf.append(" - ");
+
+            buf.append(" [");
+            if (_logDateCache != null)
+                buf.append(_logDateCache.format(request.getTimeStamp()));
+            else
+                buf.append(request.getTimeStampBuffer().toString());
+
+            buf.append("] \"");
+            buf.append(request.getMethod());
+            buf.append(' ');
+            buf.append(request.getUri().toString());
+            buf.append(' ');
+            buf.append(request.getProtocol());
+            buf.append("\" ");
+            if (request.getAsyncContinuation().isInitial())
+            {
+                int status = response.getStatus();
+                if (status <= 0)
+                    status = 404;
+                buf.append((char)('0' + ((status / 100) % 10)));
+                buf.append((char)('0' + ((status / 10) % 10)));
+                buf.append((char)('0' + (status % 10)));
+            }
+            else
+                buf.append("Async");
+
+            long responseLength = response.getContentCount();
+            if (responseLength >= 0)
+            {
+                buf.append(' ');
+                if (responseLength > 99999)
+                    buf.append(responseLength);
+                else
+                {
+                    if (responseLength > 9999)
+                        buf.append((char)('0' + ((responseLength / 10000) % 10)));
+                    if (responseLength > 999)
+                        buf.append((char)('0' + ((responseLength / 1000) % 10)));
+                    if (responseLength > 99)
+                        buf.append((char)('0' + ((responseLength / 100) % 10)));
+                    if (responseLength > 9)
+                        buf.append((char)('0' + ((responseLength / 10) % 10)));
+                    buf.append((char)('0' + (responseLength) % 10));
+                }
+                buf.append(' ');
+            }
+            else
+                buf.append(" - ");
+
+            
+            if (_extended)
+                logExtended(request, response, buf);
+
+            if (_logCookies)
+            {
+                Cookie[] cookies = request.getCookies();
+                if (cookies == null || cookies.length == 0)
+                    buf.append(" -");
+                else
+                {
+                    buf.append(" \"");
+                    for (int i = 0; i < cookies.length; i++)
+                    {
+                        if (i != 0)
+                            buf.append(';');
+                        buf.append(cookies[i].getName());
+                        buf.append('=');
+                        buf.append(cookies[i].getValue());
+                    }
+                    buf.append('\"');
+                }
+            }
+
+            if (_logDispatch || _logLatency)
+            {
+                long now = System.currentTimeMillis();
+
+                if (_logDispatch)
+                {   
+                    long d = request.getDispatchTime();
+                    buf.append(' ');
+                    buf.append(now - (d==0 ? request.getTimeStamp():d));
+                }
+
+                if (_logLatency)
+                {
+                    buf.append(' ');
+                    buf.append(now - request.getTimeStamp());
+                }
+            }
+
+            buf.append(StringUtil.__LINE_SEPARATOR);
+            
+            String log = buf.toString();
+            write(log);
+        }
+        catch (IOException e)
+        {
+            LOG.warn(e);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    protected void write(String log) throws IOException 
+    {
+        synchronized(this)
+        {
+            if (_writer==null)
+                return;
+            _writer.write(log);
+            _writer.flush();
+        }
+    }
+
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Writes extended request and response information to the output stream.
+     * 
+     * @param request request object
+     * @param response response object
+     * @param b StringBuilder to write to
+     * @throws IOException
+     */
+    protected void logExtended(Request request,
+                               Response response,
+                               StringBuilder b) throws IOException
+    {
+        String referer = request.getHeader(HttpHeaders.REFERER);
+        if (referer == null)
+            b.append("\"-\" ");
+        else
+        {
+            b.append('"');
+            b.append(referer);
+            b.append("\" ");
+        }
+
+        String agent = request.getHeader(HttpHeaders.USER_AGENT);
+        if (agent == null)
+            b.append("\"-\" ");
+        else
+        {
+            b.append('"');
+            b.append(agent);
+            b.append('"');
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set up request logging and open log file.
+     * 
+     * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
+     */
+    @Override
+    protected synchronized void doStart() throws Exception
+    {
+        if (_logDateFormat != null)
+        {
+            _logDateCache = new DateCache(_logDateFormat,_logLocale);
+            _logDateCache.setTimeZoneID(_logTimeZone);
+        }
+
+        if (_filename != null)
+        {
+            _fileOut = new RolloverFileOutputStream(_filename,_append,_retainDays,TimeZone.getTimeZone(_logTimeZone),_filenameDateFormat,null);
+            _closeOut = true;
+            LOG.info("Opened " + getDatedFilename());
+        }
+        else
+            _fileOut = System.err;
+
+        _out = _fileOut;
+
+        if (_ignorePaths != null && _ignorePaths.length > 0)
+        {
+            _ignorePathMap = new PathMap();
+            for (int i = 0; i < _ignorePaths.length; i++)
+                _ignorePathMap.put(_ignorePaths[i],_ignorePaths[i]);
+        }
+        else
+            _ignorePathMap = null;
+
+        synchronized(this)
+        {
+            _writer = new OutputStreamWriter(_out);
+        }
+        super.doStart();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Close the log file and perform cleanup.
+     * 
+     * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStop()
+     */
+    @Override
+    protected void doStop() throws Exception
+    {
+        synchronized (this)
+        {
+            super.doStop();
+            try
+            {
+                if (_writer != null)
+                    _writer.flush();
+            }
+            catch (IOException e)
+            {
+                LOG.ignore(e);
+            }
+            if (_out != null && _closeOut)
+                try
+                {
+                    _out.close();
+                }
+                catch (IOException e)
+                {
+                    LOG.ignore(e);
+                }
+
+            _out = null;
+            _fileOut = null;
+            _closeOut = false;
+            _logDateCache = null;
+            _writer = null;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/Request.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,2204 @@
+//
+//  ========================================================================
+//  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.server;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.security.Principal;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.EventListener;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.AsyncEvent;
+import javax.servlet.AsyncListener;
+import javax.servlet.DispatcherType;
+import javax.servlet.MultipartConfigElement;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletRequestAttributeEvent;
+import javax.servlet.ServletRequestAttributeListener;
+import javax.servlet.ServletRequestEvent;
+import javax.servlet.ServletRequestListener;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.Part;
+
+import org.eclipse.jetty.continuation.Continuation;
+import org.eclipse.jetty.continuation.ContinuationListener;
+import org.eclipse.jetty.http.HttpCookie;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpHeaders;
+import org.eclipse.jetty.http.HttpMethods;
+import org.eclipse.jetty.http.HttpParser;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.HttpURI;
+import org.eclipse.jetty.http.HttpVersions;
+import org.eclipse.jetty.http.MimeTypes;
+import org.eclipse.jetty.io.Buffer;
+import org.eclipse.jetty.io.BufferUtil;
+import org.eclipse.jetty.io.ByteArrayBuffer;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.nio.DirectNIOBuffer;
+import org.eclipse.jetty.io.nio.IndirectNIOBuffer;
+import org.eclipse.jetty.io.nio.NIOBuffer;
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.server.handler.ContextHandler.Context;
+import org.eclipse.jetty.util.Attributes;
+import org.eclipse.jetty.util.AttributesMap;
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.LazyList;
+import org.eclipse.jetty.util.MultiException;
+import org.eclipse.jetty.util.MultiMap;
+import org.eclipse.jetty.util.MultiPartInputStream;
+import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.URIUtil;
+import org.eclipse.jetty.util.UrlEncoded;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/* ------------------------------------------------------------ */
+/**
+ * Jetty Request.
+ * <p>
+ * Implements {@link javax.servlet.http.HttpServletRequest} from the <code>javax.servlet.http</code> package.
+ * </p>
+ * <p>
+ * The standard interface of mostly getters, is extended with setters so that the request is mutable by the handlers that it is passed to. This allows the
+ * request object to be as lightweight as possible and not actually implement any significant behavior. For example
+ * <ul>
+ *
+ * <li>The {@link Request#getContextPath()} method will return null, until the request has been passed to a {@link ContextHandler} which matches the
+ * {@link Request#getPathInfo()} with a context path and calls {@link Request#setContextPath(String)} as a result.</li>
+ *
+ * <li>the HTTP session methods will all return null sessions until such time as a request has been passed to a
+ * {@link org.eclipse.jetty.server.session.SessionHandler} which checks for session cookies and enables the ability to create new sessions.</li>
+ *
+ * <li>The {@link Request#getServletPath()} method will return null until the request has been passed to a <code>org.eclipse.jetty.servlet.ServletHandler</code>
+ * and the pathInfo matched against the servlet URL patterns and {@link Request#setServletPath(String)} called as a result.</li>
+ * </ul>
+ *
+ * A request instance is created for each {@link AbstractHttpConnection} accepted by the server and recycled for each HTTP request received via that connection.
+ * An effort is made to avoid reparsing headers and cookies that are likely to be the same for requests from the same connection.
+ *
+ * <p>
+ * The form content that a request can process is limited to protect from Denial of Service attacks. The size in bytes is limited by
+ * {@link ContextHandler#getMaxFormContentSize()} or if there is no context then the "org.eclipse.jetty.server.Request.maxFormContentSize" {@link Server}
+ * attribute. The number of parameters keys is limited by {@link ContextHandler#getMaxFormKeys()} or if there is no context then the
+ * "org.eclipse.jetty.server.Request.maxFormKeys" {@link Server} attribute.
+ *
+ *
+ */
+public class Request implements HttpServletRequest
+{
+    public static final String __MULTIPART_CONFIG_ELEMENT = "org.eclipse.multipartConfig";
+    public static final String __MULTIPART_INPUT_STREAM = "org.eclipse.multiPartInputStream";
+    public static final String __MULTIPART_CONTEXT = "org.eclipse.multiPartContext";
+    private static final Logger LOG = Log.getLogger(Request.class);
+
+    private static final String __ASYNC_FWD = "org.eclipse.asyncfwd";
+    private static final Collection __defaultLocale = Collections.singleton(Locale.getDefault());
+    private static final int __NONE = 0, _STREAM = 1, __READER = 2;
+
+    public static class MultiPartCleanerListener implements ServletRequestListener
+    {
+
+        @Override
+        public void requestDestroyed(ServletRequestEvent sre)
+        {
+            //Clean up any tmp files created by MultiPartInputStream
+            MultiPartInputStream mpis = (MultiPartInputStream)sre.getServletRequest().getAttribute(__MULTIPART_INPUT_STREAM);
+            if (mpis != null)
+            {
+                ContextHandler.Context context = (ContextHandler.Context)sre.getServletRequest().getAttribute(__MULTIPART_CONTEXT);
+
+                //Only do the cleanup if we are exiting from the context in which a servlet parsed the multipart files
+                if (context == sre.getServletContext())
+                {
+                    try
+                    {
+                        mpis.deleteParts();
+                    }
+                    catch (MultiException e)
+                    {
+                        sre.getServletContext().log("Errors deleting multipart tmp files", e);
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void requestInitialized(ServletRequestEvent sre)
+        {
+            //nothing to do, multipart config set up by ServletHolder.handle()
+        }
+        
+    }
+    
+    
+    /* ------------------------------------------------------------ */
+    public static Request getRequest(HttpServletRequest request)
+    {
+        if (request instanceof Request)
+            return (Request)request;
+
+        return AbstractHttpConnection.getCurrentConnection().getRequest();
+    }
+    protected final AsyncContinuation _async = new AsyncContinuation();
+    private boolean _asyncSupported = true;
+    private volatile Attributes _attributes;
+    private Authentication _authentication;
+    private MultiMap<String> _baseParameters;
+    private String _characterEncoding;
+    protected AbstractHttpConnection _connection;
+    private ContextHandler.Context _context;
+    private boolean _newContext;
+    private String _contextPath;
+    private CookieCutter _cookies;
+    private boolean _cookiesExtracted = false;
+    private DispatcherType _dispatcherType;
+    private boolean _dns = false;
+    private EndPoint _endp;
+    private boolean _handled = false;
+    private int _inputState = __NONE;
+    private String _method;
+    private MultiMap<String> _parameters;
+    private boolean _paramsExtracted;
+    private String _pathInfo;
+    private int _port;
+    private String _protocol = HttpVersions.HTTP_1_1;
+    private String _queryEncoding;
+    private String _queryString;
+    private BufferedReader _reader;
+    private String _readerEncoding;
+    private String _remoteAddr;
+    private String _remoteHost;
+    private Object _requestAttributeListeners;
+    private String _requestedSessionId;
+    private boolean _requestedSessionIdFromCookie = false;
+    private String _requestURI;
+    private Map<Object, HttpSession> _savedNewSessions;
+    private String _scheme = URIUtil.HTTP;
+    private UserIdentity.Scope _scope;
+    private String _serverName;
+    private String _servletPath;
+    private HttpSession _session;
+    private SessionManager _sessionManager;
+    private long _timeStamp;
+    private long _dispatchTime;
+
+    private Buffer _timeStampBuffer;
+    private HttpURI _uri;
+    
+    private MultiPartInputStream _multiPartInputStream; //if the request is a multi-part mime
+    
+    /* ------------------------------------------------------------ */
+    public Request()
+    {
+    }
+
+    /* ------------------------------------------------------------ */
+    public Request(AbstractHttpConnection connection)
+    {
+        setConnection(connection);
+    }
+
+    /* ------------------------------------------------------------ */
+    public void addEventListener(final EventListener listener)
+    {
+        if (listener instanceof ServletRequestAttributeListener)
+            _requestAttributeListeners = LazyList.add(_requestAttributeListeners,listener);
+        if (listener instanceof ContinuationListener)
+            throw new IllegalArgumentException(listener.getClass().toString());
+        if (listener instanceof AsyncListener)
+            throw new IllegalArgumentException(listener.getClass().toString());
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Extract Parameters from query string and/or form _content.
+     */
+    public void extractParameters()
+    {
+        if (_baseParameters == null)
+            _baseParameters = new MultiMap(16);
+
+        if (_paramsExtracted)
+        {
+            if (_parameters == null)
+                _parameters = _baseParameters;
+            return;
+        }
+
+        _paramsExtracted = true;
+
+        try
+        {
+            // Handle query string
+            if (_uri != null && _uri.hasQuery())
+            {
+                if (_queryEncoding == null)
+                    _uri.decodeQueryTo(_baseParameters);
+                else
+                {
+                    try
+                    {
+                        _uri.decodeQueryTo(_baseParameters,_queryEncoding);
+                    }
+                    catch (UnsupportedEncodingException e)
+                    {
+                        if (LOG.isDebugEnabled())
+                            LOG.warn(e);
+                        else
+                            LOG.warn(e.toString());
+                    }
+                }
+            }
+
+            // handle any _content.
+            String encoding = getCharacterEncoding();
+            String content_type = getContentType();
+            if (content_type != null && content_type.length() > 0)
+            {
+                content_type = HttpFields.valueParameters(content_type,null);
+
+                if (MimeTypes.FORM_ENCODED.equalsIgnoreCase(content_type) && _inputState == __NONE
+                        && (HttpMethods.POST.equals(getMethod()) || HttpMethods.PUT.equals(getMethod())))
+                {
+                    int content_length = getContentLength();
+                    if (content_length != 0)
+                    {
+                        try
+                        {
+                            int maxFormContentSize = -1;
+                            int maxFormKeys = -1;
+
+                            if (_context != null)
+                            {
+                                maxFormContentSize = _context.getContextHandler().getMaxFormContentSize();
+                                maxFormKeys = _context.getContextHandler().getMaxFormKeys();
+                            }
+                            
+                            if (maxFormContentSize < 0)
+                            {
+                                Object obj = _connection.getConnector().getServer().getAttribute("org.eclipse.jetty.server.Request.maxFormContentSize");
+                                if (obj == null)
+                                    maxFormContentSize = 200000;
+                                else if (obj instanceof Number)
+                                {                      
+                                    Number size = (Number)obj;
+                                    maxFormContentSize = size.intValue();
+                                }
+                                else if (obj instanceof String)
+                                {
+                                    maxFormContentSize = Integer.valueOf((String)obj);
+                                }
+                            }
+                            
+                            if (maxFormKeys < 0)
+                            {
+                                Object obj = _connection.getConnector().getServer().getAttribute("org.eclipse.jetty.server.Request.maxFormKeys");
+                                if (obj == null)
+                                    maxFormKeys = 1000;
+                                else if (obj instanceof Number)
+                                {
+                                    Number keys = (Number)obj;
+                                    maxFormKeys = keys.intValue();
+                                }
+                                else if (obj instanceof String)
+                                {
+                                    maxFormKeys = Integer.valueOf((String)obj);
+                                }
+                            }
+
+                            if (content_length > maxFormContentSize && maxFormContentSize > 0)
+                            {
+                                throw new IllegalStateException("Form too large " + content_length + ">" + maxFormContentSize);
+                            }
+                            InputStream in = getInputStream();
+
+                            // Add form params to query params
+                            UrlEncoded.decodeTo(in,_baseParameters,encoding,content_length < 0?maxFormContentSize:-1,maxFormKeys);
+                        }
+                        catch (IOException e)
+                        {
+                            if (LOG.isDebugEnabled())
+                                LOG.warn(e);
+                            else
+                                LOG.warn(e.toString());
+                        }
+                    }
+                }
+              
+            }
+
+            if (_parameters == null)
+                _parameters = _baseParameters;
+            else if (_parameters != _baseParameters)
+            {
+                // Merge parameters (needed if parameters extracted after a forward).
+                Iterator iter = _baseParameters.entrySet().iterator();
+                while (iter.hasNext())
+                {
+                    Map.Entry entry = (Map.Entry)iter.next();
+                    String name = (String)entry.getKey();
+                    Object values = entry.getValue();
+                    for (int i = 0; i < LazyList.size(values); i++)
+                        _parameters.add(name,LazyList.get(values,i));
+                }
+            }
+
+            if (content_type != null && content_type.length()>0 && content_type.startsWith("multipart/form-data") && getAttribute(__MULTIPART_CONFIG_ELEMENT)!=null)
+            {
+                try
+                {
+                    getParts();
+                }
+                catch (IOException e)
+                {
+                    if (LOG.isDebugEnabled())
+                        LOG.warn(e);
+                    else
+                        LOG.warn(e.toString());
+                }
+                catch (ServletException e)
+                {
+                    if (LOG.isDebugEnabled())
+                        LOG.warn(e);
+                    else
+                        LOG.warn(e.toString());
+                }
+            }
+        }
+        finally
+        {
+            // ensure params always set (even if empty) after extraction
+            if (_parameters == null)
+                _parameters = _baseParameters;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public AsyncContext getAsyncContext()
+    {
+        if (_async.isInitial() && !_async.isAsyncStarted())
+            throw new IllegalStateException(_async.getStatusString());
+        return _async;
+    }
+
+    /* ------------------------------------------------------------ */
+    public AsyncContinuation getAsyncContinuation()
+    {
+        return _async;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletRequest#getAttribute(java.lang.String)
+     */
+    public Object getAttribute(String name)
+    {
+        if ("org.eclipse.jetty.io.EndPoint.maxIdleTime".equalsIgnoreCase(name))
+            return new Long(getConnection().getEndPoint().getMaxIdleTime());
+
+        Object attr = (_attributes == null)?null:_attributes.getAttribute(name);
+        if (attr == null && Continuation.ATTRIBUTE.equals(name))
+            return _async;
+        return attr;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletRequest#getAttributeNames()
+     */
+    public Enumeration getAttributeNames()
+    {
+        if (_attributes == null)
+            return Collections.enumeration(Collections.EMPTY_LIST);
+
+        return AttributesMap.getAttributeNamesCopy(_attributes);
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     */
+    public Attributes getAttributes()
+    {
+        if (_attributes == null)
+            _attributes = new AttributesMap();
+        return _attributes;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Get the authentication.
+     *
+     * @return the authentication
+     */
+    public Authentication getAuthentication()
+    {
+        return _authentication;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletRequest#getAuthType()
+     */
+    public String getAuthType()
+    {
+        if (_authentication instanceof Authentication.Deferred)
+            setAuthentication(((Authentication.Deferred)_authentication).authenticate(this));
+        
+        if (_authentication instanceof Authentication.User)
+            return ((Authentication.User)_authentication).getAuthMethod();
+        return null;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletRequest#getCharacterEncoding()
+     */
+    public String getCharacterEncoding()
+    {
+        return _characterEncoding;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the connection.
+     */
+    public AbstractHttpConnection getConnection()
+    {
+        return _connection;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletRequest#getContentLength()
+     */
+    public int getContentLength()
+    {
+        return (int)_connection.getRequestFields().getLongField(HttpHeaders.CONTENT_LENGTH_BUFFER);
+    }
+
+    public long getContentRead()
+    {
+        if (_connection == null || _connection.getParser() == null)
+            return -1;
+
+        return ((HttpParser)_connection.getParser()).getContentRead();
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletRequest#getContentType()
+     */
+    public String getContentType()
+    {
+        return _connection.getRequestFields().getStringField(HttpHeaders.CONTENT_TYPE_BUFFER);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The current {@link Context context} used for this request, or <code>null</code> if {@link #setContext} has not yet been called.
+     */
+    public Context getContext()
+    {
+        return _context;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletRequest#getContextPath()
+     */
+    public String getContextPath()
+    {
+        return _contextPath;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletRequest#getCookies()
+     */
+    public Cookie[] getCookies()
+    {
+        if (_cookiesExtracted)
+            return _cookies == null?null:_cookies.getCookies();
+
+        _cookiesExtracted = true;
+
+        Enumeration enm = _connection.getRequestFields().getValues(HttpHeaders.COOKIE_BUFFER);
+
+        // Handle no cookies
+        if (enm != null)
+        {
+            if (_cookies == null)
+                _cookies = new CookieCutter();
+
+            while (enm.hasMoreElements())
+            {
+                String c = (String)enm.nextElement();
+                _cookies.addCookieField(c);
+            }
+        }
+
+        return _cookies == null?null:_cookies.getCookies();
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletRequest#getDateHeader(java.lang.String)
+     */
+    public long getDateHeader(String name)
+    {
+        return _connection.getRequestFields().getDateField(name);
+    }
+
+    /* ------------------------------------------------------------ */
+    public DispatcherType getDispatcherType()
+    {
+        return _dispatcherType;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletRequest#getHeader(java.lang.String)
+     */
+    public String getHeader(String name)
+    {
+        return _connection.getRequestFields().getStringField(name);
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletRequest#getHeaderNames()
+     */
+    public Enumeration getHeaderNames()
+    {
+        return _connection.getRequestFields().getFieldNames();
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletRequest#getHeaders(java.lang.String)
+     */
+    public Enumeration getHeaders(String name)
+    {
+        Enumeration e = _connection.getRequestFields().getValues(name);
+        if (e == null)
+            return Collections.enumeration(Collections.EMPTY_LIST);
+        return e;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the inputState.
+     */
+    public int getInputState()
+    {
+        return _inputState;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletRequest#getInputStream()
+     */
+    public ServletInputStream getInputStream() throws IOException
+    {
+        if (_inputState != __NONE && _inputState != _STREAM)
+            throw new IllegalStateException("READER");
+        _inputState = _STREAM;
+        return _connection.getInputStream();
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletRequest#getIntHeader(java.lang.String)
+     */
+    public int getIntHeader(String name)
+    {
+        return (int)_connection.getRequestFields().getLongField(name);
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletRequest#getLocalAddr()
+     */
+    public String getLocalAddr()
+    {
+        return _endp == null?null:_endp.getLocalAddr();
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletRequest#getLocale()
+     */
+    public Locale getLocale()
+    {
+        Enumeration enm = _connection.getRequestFields().getValues(HttpHeaders.ACCEPT_LANGUAGE,HttpFields.__separators);
+
+        // handle no locale
+        if (enm == null || !enm.hasMoreElements())
+            return Locale.getDefault();
+
+        // sort the list in quality order
+        List acceptLanguage = HttpFields.qualityList(enm);
+        if (acceptLanguage.size() == 0)
+            return Locale.getDefault();
+
+        int size = acceptLanguage.size();
+
+        if (size > 0)
+        {
+            String language = (String)acceptLanguage.get(0);
+            language = HttpFields.valueParameters(language,null);
+            String country = "";
+            int dash = language.indexOf('-');
+            if (dash > -1)
+            {
+                country = language.substring(dash + 1).trim();
+                language = language.substring(0,dash).trim();
+            }
+            return new Locale(language,country);
+        }
+
+        return Locale.getDefault();
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletRequest#getLocales()
+     */
+    public Enumeration getLocales()
+    {
+
+        Enumeration enm = _connection.getRequestFields().getValues(HttpHeaders.ACCEPT_LANGUAGE,HttpFields.__separators);
+
+        // handle no locale
+        if (enm == null || !enm.hasMoreElements())
+            return Collections.enumeration(__defaultLocale);
+
+        // sort the list in quality order
+        List acceptLanguage = HttpFields.qualityList(enm);
+
+        if (acceptLanguage.size() == 0)
+            return Collections.enumeration(__defaultLocale);
+
+        Object langs = null;
+        int size = acceptLanguage.size();
+
+        // convert to locals
+        for (int i = 0; i < size; i++)
+        {
+            String language = (String)acceptLanguage.get(i);
+            language = HttpFields.valueParameters(language,null);
+            String country = "";
+            int dash = language.indexOf('-');
+            if (dash > -1)
+            {
+                country = language.substring(dash + 1).trim();
+                language = language.substring(0,dash).trim();
+            }
+            langs = LazyList.ensureSize(langs,size);
+            langs = LazyList.add(langs,new Locale(language,country));
+        }
+
+        if (LazyList.size(langs) == 0)
+            return Collections.enumeration(__defaultLocale);
+
+        return Collections.enumeration(LazyList.getList(langs));
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletRequest#getLocalName()
+     */
+    public String getLocalName()
+    {
+        if (_endp == null)
+            return null;
+        if (_dns)
+            return _endp.getLocalHost();
+
+        String local = _endp.getLocalAddr();
+        if (local != null && local.indexOf(':') >= 0)
+            local = "[" + local + "]";
+        return local;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletRequest#getLocalPort()
+     */
+    public int getLocalPort()
+    {
+        return _endp == null?0:_endp.getLocalPort();
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletRequest#getMethod()
+     */
+    public String getMethod()
+    {
+        return _method;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletRequest#getParameter(java.lang.String)
+     */
+    public String getParameter(String name)
+    {
+        if (!_paramsExtracted)
+            extractParameters();
+        return (String)_parameters.getValue(name,0);
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletRequest#getParameterMap()
+     */
+    public Map getParameterMap()
+    {
+        if (!_paramsExtracted)
+            extractParameters();
+
+        return Collections.unmodifiableMap(_parameters.toStringArrayMap());
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletRequest#getParameterNames()
+     */
+    public Enumeration getParameterNames()
+    {
+        if (!_paramsExtracted)
+            extractParameters();
+        return Collections.enumeration(_parameters.keySet());
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the parameters.
+     */
+    public MultiMap<String> getParameters()
+    {
+        return _parameters;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletRequest#getParameterValues(java.lang.String)
+     */
+    public String[] getParameterValues(String name)
+    {
+        if (!_paramsExtracted)
+            extractParameters();
+        List<Object> vals = _parameters.getValues(name);
+        if (vals == null)
+            return null;
+        return vals.toArray(new String[vals.size()]);
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletRequest#getPathInfo()
+     */
+    public String getPathInfo()
+    {
+        return _pathInfo;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletRequest#getPathTranslated()
+     */
+    public String getPathTranslated()
+    {
+        if (_pathInfo == null || _context == null)
+            return null;
+        return _context.getRealPath(_pathInfo);
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletRequest#getProtocol()
+     */
+    public String getProtocol()
+    {
+        return _protocol;
+    }
+
+    /* ------------------------------------------------------------ */
+    public String getQueryEncoding()
+    {
+        return _queryEncoding;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletRequest#getQueryString()
+     */
+    public String getQueryString()
+    {
+        if (_queryString == null && _uri != null)
+        {
+            if (_queryEncoding == null)
+                _queryString = _uri.getQuery();
+            else
+                _queryString = _uri.getQuery(_queryEncoding);
+        }
+        return _queryString;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletRequest#getReader()
+     */
+    public BufferedReader getReader() throws IOException
+    {
+        if (_inputState != __NONE && _inputState != __READER)
+            throw new IllegalStateException("STREAMED");
+
+        if (_inputState == __READER)
+            return _reader;
+
+        String encoding = getCharacterEncoding();
+        if (encoding == null)
+            encoding = StringUtil.__ISO_8859_1;
+
+        if (_reader == null || !encoding.equalsIgnoreCase(_readerEncoding))
+        {
+            final ServletInputStream in = getInputStream();
+            _readerEncoding = encoding;
+            _reader = new BufferedReader(new InputStreamReader(in,encoding))
+            {
+                @Override
+                public void close() throws IOException
+                {
+                    in.close();
+                }
+            };
+        }
+        _inputState = __READER;
+        return _reader;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletRequest#getRealPath(java.lang.String)
+     */
+    public String getRealPath(String path)
+    {
+        if (_context == null)
+            return null;
+        return _context.getRealPath(path);
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletRequest#getRemoteAddr()
+     */
+    public String getRemoteAddr()
+    {
+        if (_remoteAddr != null)
+            return _remoteAddr;
+        return _endp == null?null:_endp.getRemoteAddr();
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletRequest#getRemoteHost()
+     */
+    public String getRemoteHost()
+    {
+        if (_dns)
+        {
+            if (_remoteHost != null)
+            {
+                return _remoteHost;
+            }
+            return _endp == null?null:_endp.getRemoteHost();
+        }
+        return getRemoteAddr();
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletRequest#getRemotePort()
+     */
+    public int getRemotePort()
+    {
+        return _endp == null?0:_endp.getRemotePort();
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletRequest#getRemoteUser()
+     */
+    public String getRemoteUser()
+    {
+        Principal p = getUserPrincipal();
+        if (p == null)
+            return null;
+        return p.getName();
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletRequest#getRequestDispatcher(java.lang.String)
+     */
+    public RequestDispatcher getRequestDispatcher(String path)
+    {
+        if (path == null || _context == null)
+            return null;
+
+        // handle relative path
+        if (!path.startsWith("/"))
+        {
+            String relTo = URIUtil.addPaths(_servletPath,_pathInfo);
+            int slash = relTo.lastIndexOf("/");
+            if (slash > 1)
+                relTo = relTo.substring(0,slash + 1);
+            else
+                relTo = "/";
+            path = URIUtil.addPaths(relTo,path);
+        }
+
+        return _context.getRequestDispatcher(path);
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletRequest#getRequestedSessionId()
+     */
+    public String getRequestedSessionId()
+    {
+        return _requestedSessionId;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletRequest#getRequestURI()
+     */
+    public String getRequestURI()
+    {
+        if (_requestURI == null && _uri != null)
+            _requestURI = _uri.getPathAndParam();
+        return _requestURI;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletRequest#getRequestURL()
+     */
+    public StringBuffer getRequestURL()
+    {
+        final StringBuffer url = new StringBuffer(48);
+        synchronized (url)
+        {
+            String scheme = getScheme();
+            int port = getServerPort();
+
+            url.append(scheme);
+            url.append("://");
+            url.append(getServerName());
+            if (_port > 0 && ((scheme.equalsIgnoreCase(URIUtil.HTTP) && port != 80) || (scheme.equalsIgnoreCase(URIUtil.HTTPS) && port != 443)))
+            {
+                url.append(':');
+                url.append(_port);
+            }
+
+            url.append(getRequestURI());
+            return url;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public Response getResponse()
+    {
+        return _connection._response;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Reconstructs the URL the client used to make the request. The returned URL contains a protocol, server name, port number, and, but it does not include a
+     * path.
+     * <p>
+     * Because this method returns a <code>StringBuffer</code>, not a string, you can modify the URL easily, for example, to append path and query parameters.
+     *
+     * This method is useful for creating redirect messages and for reporting errors.
+     *
+     * @return "scheme://host:port"
+     */
+    public StringBuilder getRootURL()
+    {
+        StringBuilder url = new StringBuilder(48);
+        String scheme = getScheme();
+        int port = getServerPort();
+
+        url.append(scheme);
+        url.append("://");
+        url.append(getServerName());
+
+        if (port > 0 && ((scheme.equalsIgnoreCase("http") && port != 80) || (scheme.equalsIgnoreCase("https") && port != 443)))
+        {
+            url.append(':');
+            url.append(port);
+        }
+        return url;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletRequest#getScheme()
+     */
+    public String getScheme()
+    {
+        return _scheme;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletRequest#getServerName()
+     */
+    public String getServerName()
+    {
+        // Return already determined host
+        if (_serverName != null)
+            return _serverName;
+
+        if (_uri == null)
+            throw new IllegalStateException("No uri");
+
+        // Return host from absolute URI
+        _serverName = _uri.getHost();
+        _port = _uri.getPort();
+        if (_serverName != null)
+            return _serverName;
+
+        // Return host from header field
+        Buffer hostPort = _connection.getRequestFields().get(HttpHeaders.HOST_BUFFER);
+        if (hostPort != null)
+        {
+            loop: for (int i = hostPort.putIndex(); i-- > hostPort.getIndex();)
+            {
+                char ch = (char)(0xff & hostPort.peek(i));
+                switch (ch)
+                {
+                    case ']':
+                        break loop;
+
+                    case ':':
+                        _serverName = BufferUtil.to8859_1_String(hostPort.peek(hostPort.getIndex(),i - hostPort.getIndex()));
+                        try
+                        {
+                            _port = BufferUtil.toInt(hostPort.peek(i + 1,hostPort.putIndex() - i - 1));
+                        }
+                        catch (NumberFormatException e)
+                        {
+                            try
+                            {
+                                if (_connection != null)
+                                    _connection._generator.sendError(HttpStatus.BAD_REQUEST_400,"Bad Host header",null,true);
+                            }
+                            catch (IOException e1)
+                            {
+                                throw new RuntimeException(e1);
+                            }
+                        }
+                        return _serverName;
+                }
+            }
+            if (_serverName == null || _port < 0)
+            {
+                _serverName = BufferUtil.to8859_1_String(hostPort);
+                _port = 0;
+            }
+
+            return _serverName;
+        }
+
+        // Return host from connection
+        if (_connection != null)
+        {
+            _serverName = getLocalName();
+            _port = getLocalPort();
+            if (_serverName != null && !StringUtil.ALL_INTERFACES.equals(_serverName))
+                return _serverName;
+        }
+
+        // Return the local host
+        try
+        {
+            _serverName = InetAddress.getLocalHost().getHostAddress();
+        }
+        catch (java.net.UnknownHostException e)
+        {
+            LOG.ignore(e);
+        }
+        return _serverName;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletRequest#getServerPort()
+     */
+    public int getServerPort()
+    {
+        if (_port <= 0)
+        {
+            if (_serverName == null)
+                getServerName();
+
+            if (_port <= 0)
+            {
+                if (_serverName != null && _uri != null)
+                    _port = _uri.getPort();
+                else
+                    _port = _endp == null?0:_endp.getLocalPort();
+            }
+        }
+
+        if (_port <= 0)
+        {
+            if (getScheme().equalsIgnoreCase(URIUtil.HTTPS))
+                return 443;
+            return 80;
+        }
+        return _port;
+    }
+
+    /* ------------------------------------------------------------ */
+    public ServletContext getServletContext()
+    {
+        return _context;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     */
+    public String getServletName()
+    {
+        if (_scope != null)
+            return _scope.getName();
+        return null;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletRequest#getServletPath()
+     */
+    public String getServletPath()
+    {
+        if (_servletPath == null)
+            _servletPath = "";
+        return _servletPath;
+    }
+
+    /* ------------------------------------------------------------ */
+    public ServletResponse getServletResponse()
+    {
+        return _connection.getResponse();
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletRequest#getSession()
+     */
+    public HttpSession getSession()
+    {
+        return getSession(true);
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletRequest#getSession(boolean)
+     */
+    public HttpSession getSession(boolean create)
+    {
+        if (_session != null)
+        {
+            if (_sessionManager != null && !_sessionManager.isValid(_session))
+                _session = null;
+            else
+                return _session;
+        }
+
+        if (!create)
+            return null;
+
+        if (_sessionManager == null)
+            throw new IllegalStateException("No SessionManager");
+
+        _session = _sessionManager.newHttpSession(this);
+        HttpCookie cookie = _sessionManager.getSessionCookie(_session,getContextPath(),isSecure());
+        if (cookie != null)
+            _connection.getResponse().addCookie(cookie);
+
+        return _session;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the sessionManager.
+     */
+    public SessionManager getSessionManager()
+    {
+        return _sessionManager;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Get Request TimeStamp
+     *
+     * @return The time that the request was received.
+     */
+    public long getTimeStamp()
+    {
+        return _timeStamp;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Get Request TimeStamp
+     *
+     * @return The time that the request was received.
+     */
+    public Buffer getTimeStampBuffer()
+    {
+        if (_timeStampBuffer == null && _timeStamp > 0)
+            _timeStampBuffer = HttpFields.__dateCache.formatBuffer(_timeStamp);
+        return _timeStampBuffer;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the uri.
+     */
+    public HttpURI getUri()
+    {
+        return _uri;
+    }
+
+    /* ------------------------------------------------------------ */
+    public UserIdentity getUserIdentity()
+    {
+        if (_authentication instanceof Authentication.Deferred)
+            setAuthentication(((Authentication.Deferred)_authentication).authenticate(this));
+
+        if (_authentication instanceof Authentication.User)
+            return ((Authentication.User)_authentication).getUserIdentity();
+        return null;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The resolved user Identity, which may be null if the {@link Authentication} is not {@link Authentication.User} (eg.
+     *         {@link Authentication.Deferred}).
+     */
+    public UserIdentity getResolvedUserIdentity()
+    {
+        if (_authentication instanceof Authentication.User)
+            return ((Authentication.User)_authentication).getUserIdentity();
+        return null;
+    }
+
+    /* ------------------------------------------------------------ */
+    public UserIdentity.Scope getUserIdentityScope()
+    {
+        return _scope;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletRequest#getUserPrincipal()
+     */
+    public Principal getUserPrincipal()
+    {
+        if (_authentication instanceof Authentication.Deferred)
+            setAuthentication(((Authentication.Deferred)_authentication).authenticate(this));
+
+        if (_authentication instanceof Authentication.User)
+        {
+            UserIdentity user = ((Authentication.User)_authentication).getUserIdentity();
+            return user.getUserPrincipal();
+        }
+        
+        return null;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Get timestamp of the request dispatch
+     *
+     * @return timestamp
+     */
+    public long getDispatchTime()
+    {
+        return _dispatchTime;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isHandled()
+    {
+        return _handled;
+    }
+
+    public boolean isAsyncStarted()
+    {
+       return _async.isAsyncStarted();
+    }
+
+
+    /* ------------------------------------------------------------ */
+    public boolean isAsyncSupported()
+    {
+        return _asyncSupported;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletRequest#isRequestedSessionIdFromCookie()
+     */
+    public boolean isRequestedSessionIdFromCookie()
+    {
+        return _requestedSessionId != null && _requestedSessionIdFromCookie;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletRequest#isRequestedSessionIdFromUrl()
+     */
+    public boolean isRequestedSessionIdFromUrl()
+    {
+        return _requestedSessionId != null && !_requestedSessionIdFromCookie;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletRequest#isRequestedSessionIdFromURL()
+     */
+    public boolean isRequestedSessionIdFromURL()
+    {
+        return _requestedSessionId != null && !_requestedSessionIdFromCookie;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletRequest#isRequestedSessionIdValid()
+     */
+    public boolean isRequestedSessionIdValid()
+    {
+        if (_requestedSessionId == null)
+            return false;
+
+        HttpSession session = getSession(false);
+        return (session != null && _sessionManager.getSessionIdManager().getClusterId(_requestedSessionId).equals(_sessionManager.getClusterId(session)));
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletRequest#isSecure()
+     */
+    public boolean isSecure()
+    {
+        return _connection.isConfidential(this);
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletRequest#isUserInRole(java.lang.String)
+     */
+    public boolean isUserInRole(String role)
+    {
+        if (_authentication instanceof Authentication.Deferred)
+            setAuthentication(((Authentication.Deferred)_authentication).authenticate(this));
+
+        if (_authentication instanceof Authentication.User)
+            return ((Authentication.User)_authentication).isUserInRole(_scope,role);
+        return false;
+    }
+
+    /* ------------------------------------------------------------ */
+    public HttpSession recoverNewSession(Object key)
+    {
+        if (_savedNewSessions == null)
+            return null;
+        return _savedNewSessions.get(key);
+    }
+
+    /* ------------------------------------------------------------ */
+    protected void recycle()
+    {
+        if (_inputState == __READER)
+        {
+            try
+            {
+                int r = _reader.read();
+                while (r != -1)
+                    r = _reader.read();
+            }
+            catch (Exception e)
+            {
+                LOG.ignore(e);
+                _reader = null;
+            }
+        }
+
+        setAuthentication(Authentication.NOT_CHECKED);
+        _async.recycle();
+        _asyncSupported = true;
+        _handled = false;
+        if (_context != null)
+            throw new IllegalStateException("Request in context!");
+        if (_attributes != null)
+            _attributes.clearAttributes();
+        _characterEncoding = null;
+        _contextPath = null;
+        if (_cookies != null)
+            _cookies.reset();
+        _cookiesExtracted = false;
+        _context = null;
+        _serverName = null;
+        _method = null;
+        _pathInfo = null;
+        _port = 0;
+        _protocol = HttpVersions.HTTP_1_1;
+        _queryEncoding = null;
+        _queryString = null;
+        _requestedSessionId = null;
+        _requestedSessionIdFromCookie = false;
+        _session = null;
+        _sessionManager = null;
+        _requestURI = null;
+        _scope = null;
+        _scheme = URIUtil.HTTP;
+        _servletPath = null;
+        _timeStamp = 0;
+        _timeStampBuffer = null;
+        _uri = null;
+        if (_baseParameters != null)
+            _baseParameters.clear();
+        _parameters = null;
+        _paramsExtracted = false;
+        _inputState = __NONE;
+
+        if (_savedNewSessions != null)
+            _savedNewSessions.clear();
+        _savedNewSessions=null;
+        _multiPartInputStream = null;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletRequest#removeAttribute(java.lang.String)
+     */
+    public void removeAttribute(String name)
+    {
+        Object old_value = _attributes == null?null:_attributes.getAttribute(name);
+
+        if (_attributes != null)
+            _attributes.removeAttribute(name);
+
+        if (old_value != null)
+        {
+            if (_requestAttributeListeners != null)
+            {
+                final ServletRequestAttributeEvent event = new ServletRequestAttributeEvent(_context,this,name,old_value);
+                final int size = LazyList.size(_requestAttributeListeners);
+                for (int i = 0; i < size; i++)
+                {
+                    final EventListener listener = (ServletRequestAttributeListener)LazyList.get(_requestAttributeListeners,i);
+                    if (listener instanceof ServletRequestAttributeListener)
+                    {
+                        final ServletRequestAttributeListener l = (ServletRequestAttributeListener)listener;
+                        l.attributeRemoved(event);
+                    }
+                }
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public void removeEventListener(final EventListener listener)
+    {
+        _requestAttributeListeners = LazyList.remove(_requestAttributeListeners,listener);
+    }
+
+    /* ------------------------------------------------------------ */
+    public void saveNewSession(Object key, HttpSession session)
+    {
+        if (_savedNewSessions == null)
+            _savedNewSessions = new HashMap<Object, HttpSession>();
+        _savedNewSessions.put(key,session);
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setAsyncSupported(boolean supported)
+    {
+        _asyncSupported = supported;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * Set a request attribute. if the attribute name is "org.eclipse.jetty.server.server.Request.queryEncoding" then the value is also passed in a call to
+     * {@link #setQueryEncoding}. <p> if the attribute name is "org.eclipse.jetty.server.server.ResponseBuffer", then the response buffer is flushed with @{link
+     * #flushResponseBuffer} <p> if the attribute name is "org.eclipse.jetty.io.EndPoint.maxIdleTime", then the value is passed to the associated {@link
+     * EndPoint#setMaxIdleTime}.
+     *
+     * @see javax.servlet.ServletRequest#setAttribute(java.lang.String, java.lang.Object)
+     */
+    public void setAttribute(String name, Object value)
+    {
+        Object old_value = _attributes == null?null:_attributes.getAttribute(name);
+
+        if (name.startsWith("org.eclipse.jetty."))
+        {
+            if ("org.eclipse.jetty.server.Request.queryEncoding".equals(name))
+                setQueryEncoding(value == null?null:value.toString());
+            else if ("org.eclipse.jetty.server.sendContent".equals(name))
+            {
+                try
+                {
+                    ((AbstractHttpConnection.Output)getServletResponse().getOutputStream()).sendContent(value);
+                }
+                catch (IOException e)
+                {
+                    throw new RuntimeException(e);
+                }
+            }
+            else if ("org.eclipse.jetty.server.ResponseBuffer".equals(name))
+            {
+                try
+                {
+                    final ByteBuffer byteBuffer = (ByteBuffer)value;
+                    synchronized (byteBuffer)
+                    {
+                        NIOBuffer buffer = byteBuffer.isDirect()?new DirectNIOBuffer(byteBuffer,true):new IndirectNIOBuffer(byteBuffer,true);
+                        ((AbstractHttpConnection.Output)getServletResponse().getOutputStream()).sendResponse(buffer);
+                    }
+                }
+                catch (IOException e)
+                {
+                    throw new RuntimeException(e);
+                }
+            }
+            else if ("org.eclipse.jetty.io.EndPoint.maxIdleTime".equalsIgnoreCase(name))
+            {
+                try
+                {
+                    getConnection().getEndPoint().setMaxIdleTime(Integer.valueOf(value.toString()));
+                }
+                catch (IOException e)
+                {
+                    throw new RuntimeException(e);
+                }
+            }
+        }
+
+        if (_attributes == null)
+            _attributes = new AttributesMap();
+        _attributes.setAttribute(name,value);
+
+        if (_requestAttributeListeners != null)
+        {
+            final ServletRequestAttributeEvent event = new ServletRequestAttributeEvent(_context,this,name,old_value == null?value:old_value);
+            final int size = LazyList.size(_requestAttributeListeners);
+            for (int i = 0; i < size; i++)
+            {
+                final EventListener listener = (ServletRequestAttributeListener)LazyList.get(_requestAttributeListeners,i);
+                if (listener instanceof ServletRequestAttributeListener)
+                {
+                    final ServletRequestAttributeListener l = (ServletRequestAttributeListener)listener;
+
+                    if (old_value == null)
+                        l.attributeAdded(event);
+                    else if (value == null)
+                        l.attributeRemoved(event);
+                    else
+                        l.attributeReplaced(event);
+                }
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     */
+    public void setAttributes(Attributes attributes)
+    {
+        _attributes = attributes;
+    }
+
+    /* ------------------------------------------------------------ */
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set the authentication.
+     *
+     * @param authentication
+     *            the authentication to set
+     */
+    public void setAuthentication(Authentication authentication)
+    {
+        _authentication = authentication;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletRequest#setCharacterEncoding(java.lang.String)
+     */
+    public void setCharacterEncoding(String encoding) throws UnsupportedEncodingException
+    {
+        if (_inputState != __NONE)
+            return;
+
+        _characterEncoding = encoding;
+
+        // check encoding is supported
+        if (!StringUtil.isUTF8(encoding))
+            // noinspection ResultOfMethodCallIgnored
+            "".getBytes(encoding);
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletRequest#setCharacterEncoding(java.lang.String)
+     */
+    public void setCharacterEncodingUnchecked(String encoding)
+    {
+        _characterEncoding = encoding;
+    }
+
+    /* ------------------------------------------------------------ */
+    // final so we can safely call this from constructor
+    protected final void setConnection(AbstractHttpConnection connection)
+    {
+        _connection = connection;
+        _async.setConnection(connection);
+        _endp = connection.getEndPoint();
+        _dns = connection.getResolveNames();
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletRequest#getContentType()
+     */
+    public void setContentType(String contentType)
+    {
+        _connection.getRequestFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,contentType);
+
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set request context
+     *
+     * @param context
+     *            context object
+     */
+    public void setContext(Context context)
+    {
+        _newContext = _context != context;
+        _context = context;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return True if this is the first call of {@link #takeNewContext()} since the last
+     *         {@link #setContext(org.eclipse.jetty.server.handler.ContextHandler.Context)} call.
+     */
+    public boolean takeNewContext()
+    {
+        boolean nc = _newContext;
+        _newContext = false;
+        return nc;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Sets the "context path" for this request
+     *
+     * @see HttpServletRequest#getContextPath()
+     */
+    public void setContextPath(String contextPath)
+    {
+        _contextPath = contextPath;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param cookies
+     *            The cookies to set.
+     */
+    public void setCookies(Cookie[] cookies)
+    {
+        if (_cookies == null)
+            _cookies = new CookieCutter();
+        _cookies.setCookies(cookies);
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setDispatcherType(DispatcherType type)
+    {
+        _dispatcherType = type;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setHandled(boolean h)
+    {
+        _handled = h;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param method
+     *            The method to set.
+     */
+    public void setMethod(String method)
+    {
+        _method = method;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param parameters
+     *            The parameters to set.
+     */
+    public void setParameters(MultiMap<String> parameters)
+    {
+        _parameters = (parameters == null)?_baseParameters:parameters;
+        if (_paramsExtracted && _parameters == null)
+            throw new IllegalStateException();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param pathInfo
+     *            The pathInfo to set.
+     */
+    public void setPathInfo(String pathInfo)
+    {
+        _pathInfo = pathInfo;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param protocol
+     *            The protocol to set.
+     */
+    public void setProtocol(String protocol)
+    {
+        _protocol = protocol;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set the character encoding used for the query string. This call will effect the return of getQueryString and getParamaters. It must be called before any
+     * geParameter methods.
+     *
+     * The request attribute "org.eclipse.jetty.server.server.Request.queryEncoding" may be set as an alternate method of calling setQueryEncoding.
+     *
+     * @param queryEncoding
+     */
+    public void setQueryEncoding(String queryEncoding)
+    {
+        _queryEncoding = queryEncoding;
+        _queryString = null;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param queryString
+     *            The queryString to set.
+     */
+    public void setQueryString(String queryString)
+    {
+        _queryString = queryString;
+        _queryEncoding = null; //assume utf-8
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param addr
+     *            The address to set.
+     */
+    public void setRemoteAddr(String addr)
+    {
+        _remoteAddr = addr;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param host
+     *            The host to set.
+     */
+    public void setRemoteHost(String host)
+    {
+        _remoteHost = host;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param requestedSessionId
+     *            The requestedSessionId to set.
+     */
+    public void setRequestedSessionId(String requestedSessionId)
+    {
+        _requestedSessionId = requestedSessionId;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param requestedSessionIdCookie
+     *            The requestedSessionIdCookie to set.
+     */
+    public void setRequestedSessionIdFromCookie(boolean requestedSessionIdCookie)
+    {
+        _requestedSessionIdFromCookie = requestedSessionIdCookie;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param requestURI
+     *            The requestURI to set.
+     */
+    public void setRequestURI(String requestURI)
+    {
+        _requestURI = requestURI;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param scheme
+     *            The scheme to set.
+     */
+    public void setScheme(String scheme)
+    {
+        _scheme = scheme;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param host
+     *            The host to set.
+     */
+    public void setServerName(String host)
+    {
+        _serverName = host;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param port
+     *            The port to set.
+     */
+    public void setServerPort(int port)
+    {
+        _port = port;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param servletPath
+     *            The servletPath to set.
+     */
+    public void setServletPath(String servletPath)
+    {
+        _servletPath = servletPath;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param session
+     *            The session to set.
+     */
+    public void setSession(HttpSession session)
+    {
+        _session = session;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param sessionManager
+     *            The sessionManager to set.
+     */
+    public void setSessionManager(SessionManager sessionManager)
+    {
+        _sessionManager = sessionManager;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setTimeStamp(long ts)
+    {
+        _timeStamp = ts;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param uri
+     *            The uri to set.
+     */
+    public void setUri(HttpURI uri)
+    {
+        _uri = uri;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setUserIdentityScope(UserIdentity.Scope scope)
+    {
+        _scope = scope;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set timetstamp of request dispatch
+     *
+     * @param value
+     *            timestamp
+     */
+    public void setDispatchTime(long value)
+    {
+        _dispatchTime = value;
+    }
+
+    /* ------------------------------------------------------------ */
+    public AsyncContext startAsync() throws IllegalStateException
+    {
+        if (!_asyncSupported)
+            throw new IllegalStateException("!asyncSupported");
+        _async.startAsync();
+        return _async;
+    }
+
+    /* ------------------------------------------------------------ */
+    public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException
+    {
+        if (!_asyncSupported)
+            throw new IllegalStateException("!asyncSupported");
+        _async.startAsync(_context,servletRequest,servletResponse);
+        return _async;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public String toString()
+    {
+        return (_handled?"[":"(") + getMethod() + " " + _uri + (_handled?"]@":")@") + hashCode() + " " + super.toString();
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean authenticate(HttpServletResponse response) throws IOException, ServletException
+    {
+        if (_authentication instanceof Authentication.Deferred)
+        {
+            setAuthentication(((Authentication.Deferred)_authentication).authenticate(this,response));
+            return !(_authentication instanceof Authentication.ResponseSent);        
+        }
+        response.sendError(HttpStatus.UNAUTHORIZED_401);
+        return false;
+    }
+
+    /* ------------------------------------------------------------ */
+    public Part getPart(String name) throws IOException, ServletException
+    {                
+        getParts();
+        return _multiPartInputStream.getPart(name);
+    }
+
+    /* ------------------------------------------------------------ */
+    public Collection<Part> getParts() throws IOException, ServletException
+    {
+        if (getContentType() == null || !getContentType().startsWith("multipart/form-data"))
+            throw new ServletException("Content-Type != multipart/form-data");
+        
+        if (_multiPartInputStream == null)
+            _multiPartInputStream = (MultiPartInputStream)getAttribute(__MULTIPART_INPUT_STREAM);
+        
+        if (_multiPartInputStream == null)
+        {
+            MultipartConfigElement config = (MultipartConfigElement)getAttribute(__MULTIPART_CONFIG_ELEMENT);
+            
+            if (config == null)
+                throw new IllegalStateException("No multipart config for servlet");
+            
+            _multiPartInputStream = new MultiPartInputStream(getInputStream(), 
+                                                             getContentType(), config, 
+                                                             (_context != null?(File)_context.getAttribute("javax.servlet.context.tempdir"):null));
+            
+            setAttribute(__MULTIPART_INPUT_STREAM, _multiPartInputStream);
+            setAttribute(__MULTIPART_CONTEXT, _context);
+            Collection<Part> parts = _multiPartInputStream.getParts(); //causes parsing 
+            for (Part p:parts)
+            {
+                MultiPartInputStream.MultiPart mp = (MultiPartInputStream.MultiPart)p;
+                if (mp.getContentDispositionFilename() == null)
+                {
+                    //Servlet Spec 3.0 pg 23, parts without filenames must be put into init params
+                    String charset = null;
+                    if (mp.getContentType() != null)
+                        charset = MimeTypes.getCharsetFromContentType(new ByteArrayBuffer(mp.getContentType()));
+
+                    ByteArrayOutputStream os = null;
+                    InputStream is = mp.getInputStream(); //get the bytes regardless of being in memory or in temp file
+                    try
+                    {
+                        os = new ByteArrayOutputStream();
+                        IO.copy(is, os);
+                        String content=new String(os.toByteArray(),charset==null?StringUtil.__UTF8:charset);   
+                        getParameter(""); //cause params to be evaluated
+                        getParameters().add(mp.getName(), content);
+                    }
+                    finally
+                    {
+                        IO.close(os);
+                        IO.close(is);
+                    }
+                }
+            }
+        }
+
+        return _multiPartInputStream.getParts();
+    }
+
+    /* ------------------------------------------------------------ */
+    public void login(String username, String password) throws ServletException
+    {
+        if (_authentication instanceof Authentication.Deferred) 
+        {
+            _authentication=((Authentication.Deferred)_authentication).login(username,password,this);
+            if (_authentication == null)
+                throw new ServletException();
+        } 
+        else 
+        {
+            throw new ServletException("Authenticated as "+_authentication);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public void logout() throws ServletException
+    {
+        if (_authentication instanceof Authentication.User)
+            ((Authentication.User)_authentication).logout();
+        _authentication=Authentication.UNAUTHENTICATED;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Merge in a new query string. The query string is merged with the existing parameters and {@link #setParameters(MultiMap)} and
+     * {@link #setQueryString(String)} are called with the result. The merge is according to the rules of the servlet dispatch forward method.
+     *
+     * @param query
+     *            The query string to merge into the request.
+     */
+    public void mergeQueryString(String query)
+    {
+        // extract parameters from dispatch query
+        MultiMap<String> parameters = new MultiMap<String>();
+        UrlEncoded.decodeTo(query,parameters, StringUtil.__UTF8); //have to assume UTF-8 because we can't know otherwise
+
+        boolean merge_old_query = false;
+
+        // Have we evaluated parameters
+        if (!_paramsExtracted)
+            extractParameters();
+
+        // Are there any existing parameters?
+        if (_parameters != null && _parameters.size() > 0)
+        {
+            // Merge parameters; new parameters of the same name take precedence.
+            Iterator<Entry<String, Object>> iter = _parameters.entrySet().iterator();
+            while (iter.hasNext())
+            {
+                Map.Entry<String, Object> entry = iter.next();
+                String name = entry.getKey();
+
+                // If the names match, we will need to remake the query string
+                if (parameters.containsKey(name))
+                    merge_old_query = true;
+
+                // Add the old values to the new parameter map
+                Object values = entry.getValue();
+                for (int i = 0; i < LazyList.size(values); i++)
+                    parameters.add(name,LazyList.get(values,i));
+            }
+        }
+
+        if (_queryString != null && _queryString.length() > 0)
+        {
+            if (merge_old_query)
+            {
+                StringBuilder overridden_query_string = new StringBuilder();
+                MultiMap<String> overridden_old_query = new MultiMap<String>();
+                UrlEncoded.decodeTo(_queryString,overridden_old_query,getQueryEncoding());//decode using any queryencoding set for the request
+                
+                
+                MultiMap<String> overridden_new_query = new MultiMap<String>();
+                UrlEncoded.decodeTo(query,overridden_new_query,StringUtil.__UTF8); //have to assume utf8 as we cannot know otherwise
+
+                Iterator<Entry<String, Object>> iter = overridden_old_query.entrySet().iterator();
+                while (iter.hasNext())
+                {
+                    Map.Entry<String, Object> entry = iter.next();
+                    String name = entry.getKey();
+                    if (!overridden_new_query.containsKey(name))
+                    {
+                        Object values = entry.getValue();
+                        for (int i = 0; i < LazyList.size(values); i++)
+                        {
+                            overridden_query_string.append("&").append(name).append("=").append((Object)LazyList.get(values,i));
+                        }
+                    }
+                }
+
+                query = query + overridden_query_string;
+            }
+            else
+            {
+                query = query + "&" + _queryString;
+            }
+        }
+
+        setParameters(parameters);
+        setQueryString(query);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/RequestLog.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,30 @@
+//
+//  ========================================================================
+//  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.server; 
+
+import org.eclipse.jetty.util.component.LifeCycle;
+
+/** 
+ * A <code>RequestLog</code> can be attached to a {@link org.eclipse.jetty.server.handler.RequestLogHandler} to enable 
+ * logging of requests/responses.
+ */
+public interface RequestLog extends LifeCycle
+{
+    public void log(Request request, Response response);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/ResourceCache.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,522 @@
+//
+//  ========================================================================
+//  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.server;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Comparator;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.jetty.http.HttpContent;
+import org.eclipse.jetty.http.HttpContent.ResourceAsHttpContent;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.MimeTypes;
+import org.eclipse.jetty.io.Buffer;
+import org.eclipse.jetty.io.ByteArrayBuffer;
+import org.eclipse.jetty.io.View;
+import org.eclipse.jetty.io.nio.DirectNIOBuffer;
+import org.eclipse.jetty.io.nio.IndirectNIOBuffer;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.resource.Resource;
+import org.eclipse.jetty.util.resource.ResourceFactory;
+
+
+/* ------------------------------------------------------------ */
+/** 
+ * 
+ */
+public class ResourceCache
+{
+    private static final Logger LOG = Log.getLogger(ResourceCache.class);
+
+    private final ConcurrentMap<String,Content> _cache;
+    private final AtomicInteger _cachedSize;
+    private final AtomicInteger _cachedFiles;
+    private final ResourceFactory _factory;
+    private final ResourceCache _parent;
+    private final MimeTypes _mimeTypes;
+    private final boolean _etags;
+
+    private boolean  _useFileMappedBuffer=true;
+    private int _maxCachedFileSize =4*1024*1024;
+    private int _maxCachedFiles=2048;
+    private int _maxCacheSize =32*1024*1024;
+    
+    /* ------------------------------------------------------------ */
+    /** Constructor.
+     * @param mimeTypes Mimetype to use for meta data
+     */
+    public ResourceCache(ResourceCache parent, ResourceFactory factory, MimeTypes mimeTypes,boolean useFileMappedBuffer,boolean etags)
+    {
+        _factory = factory;
+        _cache=new ConcurrentHashMap<String,Content>();
+        _cachedSize=new AtomicInteger();
+        _cachedFiles=new AtomicInteger();
+        _mimeTypes=mimeTypes;
+        _parent=parent;
+        _etags=etags;
+        _useFileMappedBuffer=useFileMappedBuffer;
+    }
+
+    /* ------------------------------------------------------------ */
+    public int getCachedSize()
+    {
+        return _cachedSize.get();
+    }
+    
+    /* ------------------------------------------------------------ */
+    public int getCachedFiles()
+    {
+        return _cachedFiles.get();
+    }
+    
+    /* ------------------------------------------------------------ */
+    public int getMaxCachedFileSize()
+    {
+        return _maxCachedFileSize;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setMaxCachedFileSize(int maxCachedFileSize)
+    {
+        _maxCachedFileSize = maxCachedFileSize;
+        shrinkCache();
+    }
+
+    /* ------------------------------------------------------------ */
+    public int getMaxCacheSize()
+    {
+        return _maxCacheSize;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setMaxCacheSize(int maxCacheSize)
+    {
+        _maxCacheSize = maxCacheSize;
+        shrinkCache();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the maxCachedFiles.
+     */
+    public int getMaxCachedFiles()
+    {
+        return _maxCachedFiles;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @param maxCachedFiles The maxCachedFiles to set.
+     */
+    public void setMaxCachedFiles(int maxCachedFiles)
+    {
+        _maxCachedFiles = maxCachedFiles;
+        shrinkCache();
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isUseFileMappedBuffer()
+    {
+        return _useFileMappedBuffer;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setUseFileMappedBuffer(boolean useFileMappedBuffer)
+    {
+        _useFileMappedBuffer = useFileMappedBuffer;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void flushCache()
+    {
+        if (_cache!=null)
+        {
+            while (_cache.size()>0)
+            {
+                for (String path : _cache.keySet())
+                {
+                    Content content = _cache.remove(path);
+                    if (content!=null)
+                        content.invalidate();
+                }
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Get a Entry from the cache.
+     * Get either a valid entry object or create a new one if possible.
+     *
+     * @param pathInContext The key into the cache
+     * @return The entry matching <code>pathInContext</code>, or a new entry 
+     * if no matching entry was found. If the content exists but is not cachable, 
+     * then a {@link ResourceAsHttpContent} instance is return. If 
+     * the resource does not exist, then null is returned.
+     * @throws IOException Problem loading the resource
+     */
+    public HttpContent lookup(String pathInContext)
+        throws IOException
+    {
+        // Is the content in this cache?
+        Content content =_cache.get(pathInContext);
+        if (content!=null && (content).isValid())
+            return content;
+       
+        // try loading the content from our factory.
+        Resource resource=_factory.getResource(pathInContext);
+        HttpContent loaded = load(pathInContext,resource);
+        if (loaded!=null)
+            return loaded;
+        
+        // Is the content in the parent cache?
+        if (_parent!=null)
+        {
+            HttpContent httpContent=_parent.lookup(pathInContext);
+            if (httpContent!=null)
+                return httpContent;
+        }
+        
+        return null;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @param resource
+     * @return True if the resource is cacheable. The default implementation tests the cache sizes.
+     */
+    protected boolean isCacheable(Resource resource)
+    {
+        long len = resource.length();
+
+        // Will it fit in the cache?
+        return  (len>0 && len<_maxCachedFileSize && len<_maxCacheSize);
+    }
+    
+    /* ------------------------------------------------------------ */
+    private HttpContent load(String pathInContext, Resource resource)
+        throws IOException
+    {
+        Content content=null;
+        
+        if (resource==null || !resource.exists())
+            return null;
+        
+        // Will it fit in the cache?
+        if (!resource.isDirectory() && isCacheable(resource))
+        {   
+            // Create the Content (to increment the cache sizes before adding the content 
+            content = new Content(pathInContext,resource);
+
+            // reduce the cache to an acceptable size.
+            shrinkCache();
+
+            // Add it to the cache.
+            Content added = _cache.putIfAbsent(pathInContext,content);
+            if (added!=null)
+            {
+                content.invalidate();
+                content=added;
+            }
+
+            return content;
+        }
+        
+        return new HttpContent.ResourceAsHttpContent(resource,_mimeTypes.getMimeByExtension(resource.toString()),getMaxCachedFileSize(),_etags);
+        
+    }
+    
+    /* ------------------------------------------------------------ */
+    private void shrinkCache()
+    {
+        // While we need to shrink
+        while (_cache.size()>0 && (_cachedFiles.get()>_maxCachedFiles || _cachedSize.get()>_maxCacheSize))
+        {
+            // Scan the entire cache and generate an ordered list by last accessed time.
+            SortedSet<Content> sorted= new TreeSet<Content>(
+                    new Comparator<Content>()
+                    {
+                        public int compare(Content c1, Content c2)
+                        {
+                            if (c1._lastAccessed<c2._lastAccessed)
+                                return -1;
+                            
+                            if (c1._lastAccessed>c2._lastAccessed)
+                                return 1;
+
+                            if (c1._length<c2._length)
+                                return -1;
+                            
+                            return c1._key.compareTo(c2._key);
+                        }
+                    });
+            for (Content content : _cache.values())
+                sorted.add(content);
+            
+            // Invalidate least recently used first
+            for (Content content : sorted)
+            {
+                if (_cachedFiles.get()<=_maxCachedFiles && _cachedSize.get()<=_maxCacheSize)
+                    break;
+                if (content==_cache.remove(content.getKey()))
+                    content.invalidate();
+            }
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    protected Buffer getIndirectBuffer(Resource resource)
+    {
+        try
+        {
+            int len=(int)resource.length();
+            if (len<0)
+            {
+                LOG.warn("invalid resource: "+String.valueOf(resource)+" "+len);
+                return null;
+            }
+            Buffer buffer = new IndirectNIOBuffer(len);
+            InputStream is = resource.getInputStream();
+            buffer.readFrom(is,len);
+            is.close();
+            return buffer;
+        }
+        catch(IOException e)
+        {
+            LOG.warn(e);
+            return null;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    protected Buffer getDirectBuffer(Resource resource)
+    {
+        try
+        {
+            if (_useFileMappedBuffer && resource.getFile()!=null) 
+                return new DirectNIOBuffer(resource.getFile());
+
+            int len=(int)resource.length();
+            if (len<0)
+            {
+                LOG.warn("invalid resource: "+String.valueOf(resource)+" "+len);
+                return null;
+            }
+            Buffer buffer = new DirectNIOBuffer(len);
+            InputStream is = resource.getInputStream();
+            buffer.readFrom(is,len);
+            is.close();
+            return buffer;
+        }
+        catch(IOException e)
+        {
+            LOG.warn(e);
+            return null;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public String toString()
+    {
+        return "ResourceCache["+_parent+","+_factory+"]@"+hashCode();
+    }
+    
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    /** MetaData associated with a context Resource.
+     */
+    public class Content implements HttpContent
+    {
+        final Resource _resource;
+        final int _length;
+        final String _key;
+        final long _lastModified;
+        final Buffer _lastModifiedBytes;
+        final Buffer _contentType;
+        final Buffer _etagBuffer;
+        
+        volatile long _lastAccessed;
+        AtomicReference<Buffer> _indirectBuffer=new AtomicReference<Buffer>();
+        AtomicReference<Buffer> _directBuffer=new AtomicReference<Buffer>();
+
+        /* ------------------------------------------------------------ */
+        Content(String pathInContext,Resource resource)
+        {
+            _key=pathInContext;
+            _resource=resource;
+
+            _contentType=_mimeTypes.getMimeByExtension(_resource.toString());
+            boolean exists=resource.exists();
+            _lastModified=exists?resource.lastModified():-1;
+            _lastModifiedBytes=_lastModified<0?null:new ByteArrayBuffer(HttpFields.formatDate(_lastModified));
+            
+            _length=exists?(int)resource.length():0;
+            _cachedSize.addAndGet(_length);
+            _cachedFiles.incrementAndGet();
+            _lastAccessed=System.currentTimeMillis();
+            
+            _etagBuffer=_etags?new ByteArrayBuffer(resource.getWeakETag()):null;
+        }
+
+
+        /* ------------------------------------------------------------ */
+        public String getKey()
+        {
+            return _key;
+        }
+
+        /* ------------------------------------------------------------ */
+        public boolean isCached()
+        {
+            return _key!=null;
+        }
+        
+        /* ------------------------------------------------------------ */
+        public boolean isMiss()
+        {
+            return false;
+        }
+
+        /* ------------------------------------------------------------ */
+        public Resource getResource()
+        {
+            return _resource;
+        }
+
+        /* ------------------------------------------------------------ */
+        public Buffer getETag()
+        {
+            return _etagBuffer;
+        }
+        
+        /* ------------------------------------------------------------ */
+        boolean isValid()
+        {
+            if (_lastModified==_resource.lastModified() && _length==_resource.length())
+            {
+                _lastAccessed=System.currentTimeMillis();
+                return true;
+            }
+
+            if (this==_cache.remove(_key))
+                invalidate();
+            return false;
+        }
+
+        /* ------------------------------------------------------------ */
+        protected void invalidate()
+        {
+            // Invalidate it
+            _cachedSize.addAndGet(-_length);
+            _cachedFiles.decrementAndGet();
+            _resource.release(); 
+        }
+
+        /* ------------------------------------------------------------ */
+        public Buffer getLastModified()
+        {
+            return _lastModifiedBytes;
+        }
+
+        /* ------------------------------------------------------------ */
+        public Buffer getContentType()
+        {
+            return _contentType;
+        }
+
+        /* ------------------------------------------------------------ */
+        public void release()
+        {
+            // don't release while cached. Release when invalidated.
+        }
+
+        /* ------------------------------------------------------------ */
+        public Buffer getIndirectBuffer()
+        {
+            Buffer buffer = _indirectBuffer.get();
+            if (buffer==null)
+            {
+                Buffer buffer2=ResourceCache.this.getIndirectBuffer(_resource);
+                
+                if (buffer2==null)
+                    LOG.warn("Could not load "+this);
+                else if (_indirectBuffer.compareAndSet(null,buffer2))
+                    buffer=buffer2;
+                else
+                    buffer=_indirectBuffer.get();
+            }
+            if (buffer==null)
+                return null;
+            return new View(buffer);
+        }
+        
+
+        /* ------------------------------------------------------------ */
+        public Buffer getDirectBuffer()
+        {
+            Buffer buffer = _directBuffer.get();
+            if (buffer==null)
+            {
+                Buffer buffer2=ResourceCache.this.getDirectBuffer(_resource);
+
+                if (buffer2==null)
+                    LOG.warn("Could not load "+this);
+                else if (_directBuffer.compareAndSet(null,buffer2))
+                    buffer=buffer2;
+                else
+                    buffer=_directBuffer.get();
+            }
+            if (buffer==null)
+                return null;
+                        
+            return new View(buffer);
+        }
+        
+        /* ------------------------------------------------------------ */
+        public long getContentLength()
+        {
+            return _length;
+        }
+
+        /* ------------------------------------------------------------ */
+        public InputStream getInputStream() throws IOException
+        {
+            Buffer indirect = getIndirectBuffer();
+            if (indirect!=null && indirect.array()!=null)
+                return new ByteArrayInputStream(indirect.array(),indirect.getIndex(),indirect.length());
+           
+            return _resource.getInputStream();
+        }   
+
+        /* ------------------------------------------------------------ */
+        @Override
+        public String toString()
+        {
+            return String.format("%s %s %d %s %s",_resource,_resource.exists(),_resource.lastModified(),_contentType,_lastModifiedBytes);
+        }   
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/Response.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,1307 @@
+//
+//  ========================================================================
+//  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.server;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Collection;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Enumeration;
+import java.util.Locale;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.SessionTrackingMode;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.eclipse.jetty.http.HttpCookie;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpGenerator;
+import org.eclipse.jetty.http.HttpHeaderValues;
+import org.eclipse.jetty.http.HttpHeaders;
+import org.eclipse.jetty.http.HttpSchemes;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.HttpURI;
+import org.eclipse.jetty.http.HttpVersions;
+import org.eclipse.jetty.http.MimeTypes;
+import org.eclipse.jetty.io.BufferCache.CachedBuffer;
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.server.handler.ErrorHandler;
+import org.eclipse.jetty.util.ByteArrayISO8859Writer;
+import org.eclipse.jetty.util.QuotedStringTokenizer;
+import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.URIUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/** Response.
+ * <p>
+ * Implements {@link javax.servlet.http.HttpServletResponse} from the <code>javax.servlet.http</code> package.
+ * </p>
+ */
+public class Response implements HttpServletResponse
+{
+    private static final Logger LOG = Log.getLogger(Response.class);
+
+    
+    public static final int
+        NONE=0,
+        STREAM=1,
+        WRITER=2;
+
+    /**
+     * If a header name starts with this string,  the header (stripped of the prefix)
+     * can be set during include using only {@link #setHeader(String, String)} or
+     * {@link #addHeader(String, String)}.
+     */
+    public final static String SET_INCLUDE_HEADER_PREFIX = "org.eclipse.jetty.server.include.";
+
+    /**
+     * If this string is found within the comment of a cookie added with {@link #addCookie(Cookie)}, then the cookie 
+     * will be set as HTTP ONLY.
+     */
+    public final static String HTTP_ONLY_COMMENT="__HTTP_ONLY__";
+    
+    
+    /* ------------------------------------------------------------ */
+    public static Response getResponse(HttpServletResponse response)
+    {
+        if (response instanceof Response)
+            return (Response)response;
+
+        return AbstractHttpConnection.getCurrentConnection().getResponse();
+    }
+    
+    private final AbstractHttpConnection _connection;
+    private int _status=SC_OK;
+    private String _reason;
+    private Locale _locale;
+    private String _mimeType;
+    private CachedBuffer _cachedMimeType;
+    private String _characterEncoding;
+    private boolean _explicitEncoding;
+    private String _contentType;
+    private volatile int _outputState;
+    private PrintWriter _writer;
+
+    /* ------------------------------------------------------------ */
+    /**
+     *
+     */
+    public Response(AbstractHttpConnection connection)
+    {
+        _connection=connection;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletResponse#reset()
+     */
+    protected void recycle()
+    {
+        _status=SC_OK;
+        _reason=null;
+        _locale=null;
+        _mimeType=null;
+        _cachedMimeType=null;
+        _characterEncoding=null;
+        _explicitEncoding=false;
+        _contentType=null;
+        _writer=null;
+        _outputState=NONE;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletResponse#addCookie(javax.servlet.http.Cookie)
+     */
+    public void addCookie(HttpCookie cookie)
+    {
+        _connection.getResponseFields().addSetCookie(cookie);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletResponse#addCookie(javax.servlet.http.Cookie)
+     */
+    public void addCookie(Cookie cookie)
+    {
+        String comment=cookie.getComment();
+        boolean http_only=false;
+        
+        if (comment!=null)
+        {
+            int i=comment.indexOf(HTTP_ONLY_COMMENT);
+            if (i>=0)
+            {
+                http_only=true;
+                comment=comment.replace(HTTP_ONLY_COMMENT,"").trim();
+                if (comment.length()==0)
+                    comment=null;
+            }
+        }
+        _connection.getResponseFields().addSetCookie(cookie.getName(),
+                cookie.getValue(),
+                cookie.getDomain(),
+                cookie.getPath(),
+                cookie.getMaxAge(),
+                comment,
+                cookie.getSecure(),
+                http_only || cookie.isHttpOnly(),
+                cookie.getVersion());
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletResponse#containsHeader(java.lang.String)
+     */
+    public boolean containsHeader(String name)
+    {
+        return _connection.getResponseFields().containsKey(name);
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletResponse#encodeURL(java.lang.String)
+     */
+    public String encodeURL(String url)
+    {
+        final Request request=_connection.getRequest();
+        SessionManager sessionManager = request.getSessionManager();
+        if (sessionManager==null)
+            return url;
+        
+        HttpURI uri = null;
+        if (sessionManager.isCheckingRemoteSessionIdEncoding() && URIUtil.hasScheme(url))
+        {
+            uri = new HttpURI(url);
+            String path = uri.getPath();
+            path = (path == null?"":path);
+            int port=uri.getPort();
+            if (port<0) 
+                port = HttpSchemes.HTTPS.equalsIgnoreCase(uri.getScheme())?443:80;
+            if (!request.getServerName().equalsIgnoreCase(uri.getHost()) ||
+                request.getServerPort()!=port ||
+                !path.startsWith(request.getContextPath())) //TODO the root context path is "", with which every non null string starts
+                return url;
+        }
+        
+        String sessionURLPrefix = sessionManager.getSessionIdPathParameterNamePrefix();
+        if (sessionURLPrefix==null)
+            return url;
+
+        if (url==null)
+            return null;
+        
+        // should not encode if cookies in evidence
+        if ((sessionManager.isUsingCookies() && request.isRequestedSessionIdFromCookie()) || !sessionManager.isUsingURLs()) 
+        {
+            int prefix=url.indexOf(sessionURLPrefix);
+            if (prefix!=-1)
+            {
+                int suffix=url.indexOf("?",prefix);
+                if (suffix<0)
+                    suffix=url.indexOf("#",prefix);
+
+                if (suffix<=prefix)
+                    return url.substring(0,prefix);
+                return url.substring(0,prefix)+url.substring(suffix);
+            }
+            return url;
+        }
+
+        // get session;
+        HttpSession session=request.getSession(false);
+
+        // no session
+        if (session == null)
+            return url;
+
+        // invalid session
+        if (!sessionManager.isValid(session))
+            return url;
+
+        String id=sessionManager.getNodeId(session);
+
+        if (uri == null)
+                uri = new HttpURI(url);
+     
+        
+        // Already encoded
+        int prefix=url.indexOf(sessionURLPrefix);
+        if (prefix!=-1)
+        {
+            int suffix=url.indexOf("?",prefix);
+            if (suffix<0)
+                suffix=url.indexOf("#",prefix);
+
+            if (suffix<=prefix)
+                return url.substring(0,prefix+sessionURLPrefix.length())+id;
+            return url.substring(0,prefix+sessionURLPrefix.length())+id+
+                url.substring(suffix);
+        }
+
+        // edit the session
+        int suffix=url.indexOf('?');
+        if (suffix<0)
+            suffix=url.indexOf('#');
+        if (suffix<0) 
+        {          
+            return url+ 
+                   ((HttpSchemes.HTTPS.equalsIgnoreCase(uri.getScheme()) || HttpSchemes.HTTP.equalsIgnoreCase(uri.getScheme())) && uri.getPath()==null?"/":"") + //if no path, insert the root path
+                   sessionURLPrefix+id;
+        }
+     
+        
+        return url.substring(0,suffix)+
+            ((HttpSchemes.HTTPS.equalsIgnoreCase(uri.getScheme()) || HttpSchemes.HTTP.equalsIgnoreCase(uri.getScheme())) && uri.getPath()==null?"/":"")+ //if no path so insert the root path
+            sessionURLPrefix+id+url.substring(suffix);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see javax.servlet.http.HttpServletResponse#encodeRedirectURL(java.lang.String)
+     */
+    public String encodeRedirectURL(String url)
+    {
+        return encodeURL(url);
+    }
+
+    /* ------------------------------------------------------------ */
+    @Deprecated
+    public String encodeUrl(String url)
+    {
+        return encodeURL(url);
+    }
+
+    /* ------------------------------------------------------------ */
+    @Deprecated
+    public String encodeRedirectUrl(String url)
+    {
+        return encodeRedirectURL(url);
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletResponse#sendError(int, java.lang.String)
+     */
+    public void sendError(int code, String message) throws IOException
+    {
+    	if (_connection.isIncluding())
+    		return;
+
+        if (isCommitted())
+            LOG.warn("Committed before "+code+" "+message);
+
+        resetBuffer();
+        _characterEncoding=null;
+        setHeader(HttpHeaders.EXPIRES,null);
+        setHeader(HttpHeaders.LAST_MODIFIED,null);
+        setHeader(HttpHeaders.CACHE_CONTROL,null);
+        setHeader(HttpHeaders.CONTENT_TYPE,null);
+        setHeader(HttpHeaders.CONTENT_LENGTH,null);
+
+        _outputState=NONE;
+        setStatus(code,message);
+
+        if (message==null)
+            message=HttpStatus.getMessage(code);
+
+        // If we are allowed to have a body
+        if (code!=SC_NO_CONTENT &&
+            code!=SC_NOT_MODIFIED &&
+            code!=SC_PARTIAL_CONTENT &&
+            code>=SC_OK)
+        {
+            Request request = _connection.getRequest();
+
+            ErrorHandler error_handler = null;
+            ContextHandler.Context context = request.getContext();
+            if (context!=null)
+                error_handler=context.getContextHandler().getErrorHandler();
+            if (error_handler==null)
+                error_handler = _connection.getConnector().getServer().getBean(ErrorHandler.class);
+            if (error_handler!=null)
+            {
+                request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE,new Integer(code));
+                request.setAttribute(RequestDispatcher.ERROR_MESSAGE, message);
+                request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI, request.getRequestURI());
+                request.setAttribute(RequestDispatcher.ERROR_SERVLET_NAME,request.getServletName());
+                error_handler.handle(null,_connection.getRequest(),_connection.getRequest(),this );
+            }
+            else
+            {
+                setHeader(HttpHeaders.CACHE_CONTROL, "must-revalidate,no-cache,no-store");
+                setContentType(MimeTypes.TEXT_HTML_8859_1);
+                ByteArrayISO8859Writer writer= new ByteArrayISO8859Writer(2048);
+                if (message != null)
+                {
+                    message= StringUtil.replace(message, "&", "&amp;");
+                    message= StringUtil.replace(message, "<", "&lt;");
+                    message= StringUtil.replace(message, ">", "&gt;");
+                }
+                String uri= request.getRequestURI();
+                if (uri!=null)
+                {
+                    uri= StringUtil.replace(uri, "&", "&amp;");
+                    uri= StringUtil.replace(uri, "<", "&lt;");
+                    uri= StringUtil.replace(uri, ">", "&gt;");
+                }
+
+                writer.write("<html>\n<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html;charset=ISO-8859-1\"/>\n");
+                writer.write("<title>Error ");
+                writer.write(Integer.toString(code));
+                writer.write(' ');
+                if (message==null)
+                    message=HttpStatus.getMessage(code);
+                writer.write(message);
+                writer.write("</title>\n</head>\n<body>\n<h2>HTTP ERROR: ");
+                writer.write(Integer.toString(code));
+                writer.write("</h2>\n<p>Problem accessing ");
+                writer.write(uri);
+                writer.write(". Reason:\n<pre>    ");
+                writer.write(message);
+                writer.write("</pre>");
+                writer.write("</p>\n<hr /><i><small>Powered by Jetty://</small></i>");
+
+                for (int i= 0; i < 20; i++)
+                    writer.write("\n                                                ");
+                writer.write("\n</body>\n</html>\n");
+
+                writer.flush();
+                setContentLength(writer.size());
+                writer.writeTo(getOutputStream());
+                writer.destroy();
+            }
+        }
+        else if (code!=SC_PARTIAL_CONTENT)
+        {
+            _connection.getRequestFields().remove(HttpHeaders.CONTENT_TYPE_BUFFER);
+            _connection.getRequestFields().remove(HttpHeaders.CONTENT_LENGTH_BUFFER);
+            _characterEncoding=null;
+            _mimeType=null;
+            _cachedMimeType=null;
+        }
+
+        complete();
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletResponse#sendError(int)
+     */
+    public void sendError(int sc) throws IOException
+    {
+        if (sc==102)
+            sendProcessing();
+        else
+            sendError(sc,null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /* Send a 102-Processing response.
+     * If the connection is a HTTP connection, the version is 1.1 and the
+     * request has a Expect header starting with 102, then a 102 response is
+     * sent. This indicates that the request still be processed and real response
+     * can still be sent.   This method is called by sendError if it is passed 102.
+     * @see javax.servlet.http.HttpServletResponse#sendError(int)
+     */
+    public void sendProcessing() throws IOException
+    {
+        if (_connection.isExpecting102Processing() && !isCommitted())
+            ((HttpGenerator)_connection.getGenerator()).send1xx(HttpStatus.PROCESSING_102);
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletResponse#sendRedirect(java.lang.String)
+     */
+    public void sendRedirect(String location) throws IOException
+    {
+    	if (_connection.isIncluding())
+    		return;
+
+        if (location==null)
+            throw new IllegalArgumentException();
+
+        if (!URIUtil.hasScheme(location))
+        {
+            StringBuilder buf = _connection.getRequest().getRootURL();
+            if (location.startsWith("/"))
+                buf.append(location);
+            else
+            {
+                String path=_connection.getRequest().getRequestURI();
+                String parent=(path.endsWith("/"))?path:URIUtil.parentPath(path);
+                location=URIUtil.addPaths(parent,location);
+                if(location==null)
+                    throw new IllegalStateException("path cannot be above root");
+                if (!location.startsWith("/"))
+                    buf.append('/');
+                buf.append(location);
+            }
+
+            location=buf.toString();
+            HttpURI uri = new HttpURI(location);
+            String path=uri.getDecodedPath();
+            String canonical=URIUtil.canonicalPath(path);
+            if (canonical==null)
+                throw new IllegalArgumentException();
+            if (!canonical.equals(path))
+            {
+                buf = _connection.getRequest().getRootURL();
+                buf.append(URIUtil.encodePath(canonical));
+                String param=uri.getParam();
+                if (param!=null)
+                {
+                    buf.append(';');
+                    buf.append(param);
+                }
+                String query=uri.getQuery();
+                if (query!=null)
+                {
+                    buf.append('?');
+                    buf.append(query);
+                }
+                String fragment=uri.getFragment();
+                if (fragment!=null)
+                {
+                    buf.append('#');
+                    buf.append(fragment);
+                }
+                location=buf.toString();
+            }
+        }
+        
+        resetBuffer();
+        setHeader(HttpHeaders.LOCATION,location);
+        setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
+        complete();
+
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletResponse#setDateHeader(java.lang.String, long)
+     */
+    public void setDateHeader(String name, long date)
+    {
+        if (!_connection.isIncluding())
+            _connection.getResponseFields().putDateField(name, date);
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletResponse#addDateHeader(java.lang.String, long)
+     */
+    public void addDateHeader(String name, long date)
+    {
+        if (!_connection.isIncluding())
+            _connection.getResponseFields().addDateField(name, date);
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletResponse#setHeader(java.lang.String, java.lang.String)
+     */
+    public void setHeader(String name, String value)
+    {
+        if (HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(name))
+            setContentType(value);
+        else
+        {
+            if (_connection.isIncluding())
+            {
+                if (name.startsWith(SET_INCLUDE_HEADER_PREFIX))
+                    name=name.substring(SET_INCLUDE_HEADER_PREFIX.length());
+                else
+                    return;
+            }
+            _connection.getResponseFields().put(name, value);
+            if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name))
+            {
+                if (value==null)
+                    _connection._generator.setContentLength(-1);
+                else
+                    _connection._generator.setContentLength(Long.parseLong(value));
+            }
+        }
+    }
+
+
+    /* ------------------------------------------------------------ */
+    public Collection<String> getHeaderNames()
+    {
+        final HttpFields fields=_connection.getResponseFields();
+        return fields.getFieldNamesCollection();
+    }
+    
+    /* ------------------------------------------------------------ */
+    /*
+     */
+    public String getHeader(String name)
+    {
+        return _connection.getResponseFields().getStringField(name);
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     */
+    public Collection<String> getHeaders(String name)
+    {
+        final HttpFields fields=_connection.getResponseFields();
+        Collection<String> i = fields.getValuesCollection(name);
+        if (i==null)
+            return Collections.EMPTY_LIST;
+        return i;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletResponse#addHeader(java.lang.String, java.lang.String)
+     */
+    public void addHeader(String name, String value)
+    {
+
+        if (_connection.isIncluding())
+        {
+            if (name.startsWith(SET_INCLUDE_HEADER_PREFIX))
+                name=name.substring(SET_INCLUDE_HEADER_PREFIX.length());
+            else
+                return;
+        }
+
+        if (HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(name))
+        {
+            setContentType(value);
+            return;
+        }
+        
+        _connection.getResponseFields().add(name, value);
+        if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name))
+            _connection._generator.setContentLength(Long.parseLong(value));
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletResponse#setIntHeader(java.lang.String, int)
+     */
+    public void setIntHeader(String name, int value)
+    {
+        if (!_connection.isIncluding())
+        {
+            _connection.getResponseFields().putLongField(name, value);
+            if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name))
+                _connection._generator.setContentLength(value);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletResponse#addIntHeader(java.lang.String, int)
+     */
+    public void addIntHeader(String name, int value)
+    {
+        if (!_connection.isIncluding())
+        {
+            _connection.getResponseFields().addLongField(name, value);
+            if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name))
+                _connection._generator.setContentLength(value);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletResponse#setStatus(int)
+     */
+    public void setStatus(int sc)
+    {
+        setStatus(sc,null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpServletResponse#setStatus(int, java.lang.String)
+     */
+    public void setStatus(int sc, String sm)
+    {
+        if (sc<=0)
+            throw new IllegalArgumentException();
+        if (!_connection.isIncluding())
+        {
+            _status=sc;
+            _reason=sm;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletResponse#getCharacterEncoding()
+     */
+    public String getCharacterEncoding()
+    {
+        if (_characterEncoding==null)
+            _characterEncoding=StringUtil.__ISO_8859_1;
+        return _characterEncoding;
+    }
+    
+    /* ------------------------------------------------------------ */
+    String getSetCharacterEncoding()
+    {
+        return _characterEncoding;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletResponse#getContentType()
+     */
+    public String getContentType()
+    {
+        return _contentType;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletResponse#getOutputStream()
+     */
+    public ServletOutputStream getOutputStream() throws IOException
+    {
+        if (_outputState!=NONE && _outputState!=STREAM)
+            throw new IllegalStateException("WRITER");
+
+        ServletOutputStream out = _connection.getOutputStream();
+        _outputState=STREAM;
+        return out;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isWriting()
+    {
+        return _outputState==WRITER;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isOutputing()
+    {
+        return _outputState!=NONE;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletResponse#getWriter()
+     */
+    public PrintWriter getWriter() throws IOException
+    {
+        if (_outputState!=NONE && _outputState!=WRITER)
+            throw new IllegalStateException("STREAM");
+
+        /* if there is no writer yet */
+        if (_writer==null)
+        {
+            /* get encoding from Content-Type header */
+            String encoding = _characterEncoding;
+
+            if (encoding==null)
+            {
+                /* implementation of educated defaults */
+                if(_cachedMimeType != null)
+                    encoding = MimeTypes.getCharsetFromContentType(_cachedMimeType);
+
+                if (encoding==null)
+                    encoding = StringUtil.__ISO_8859_1;
+
+                setCharacterEncoding(encoding);
+            }
+
+            /* construct Writer using correct encoding */
+            _writer = _connection.getPrintWriter(encoding);
+        }
+        _outputState=WRITER;
+        return _writer;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletResponse#setCharacterEncoding(java.lang.String)
+     */
+    public void setCharacterEncoding(String encoding)
+    {
+    	if (_connection.isIncluding())
+    		return;
+
+        if (this._outputState==0 && !isCommitted())
+        {
+            _explicitEncoding=true;
+
+            if (encoding==null)
+            {
+                // Clear any encoding.
+                if (_characterEncoding!=null)
+                {
+                    _characterEncoding=null;
+                    if (_cachedMimeType!=null)
+                        _contentType=_cachedMimeType.toString();
+                    else if (_mimeType!=null)
+                        _contentType=_mimeType;
+                    else
+                        _contentType=null;
+
+                    if (_contentType==null)
+                        _connection.getResponseFields().remove(HttpHeaders.CONTENT_TYPE_BUFFER);
+                    else
+                        _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
+                }
+            }
+            else
+            {
+                // No, so just add this one to the mimetype
+                _characterEncoding=encoding;
+                if (_contentType!=null)
+                {
+                    int i0=_contentType.indexOf(';');
+                    if (i0<0)
+                    {
+                        _contentType=null;
+                        if(_cachedMimeType!=null)
+                        {
+                            CachedBuffer content_type = _cachedMimeType.getAssociate(_characterEncoding);
+                            if (content_type!=null)
+                            {
+                                _contentType=content_type.toString();
+                                _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,content_type);
+                            }
+                        }
+
+                        if (_contentType==null)
+                        {
+                            _contentType = _mimeType+";charset="+QuotedStringTokenizer.quoteIfNeeded(_characterEncoding,";= ");
+                            _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
+                        }
+                    }
+                    else
+                    {
+                        int i1=_contentType.indexOf("charset=",i0);
+                        if (i1<0)
+                        {
+                            _contentType = _contentType+";charset="+QuotedStringTokenizer.quoteIfNeeded(_characterEncoding,";= ");
+                        }
+                        else
+                        {
+                            int i8=i1+8;
+                            int i2=_contentType.indexOf(" ",i8);
+                            if (i2<0)
+                                _contentType=_contentType.substring(0,i8)+QuotedStringTokenizer.quoteIfNeeded(_characterEncoding,";= ");
+                            else
+                                _contentType=_contentType.substring(0,i8)+QuotedStringTokenizer.quoteIfNeeded(_characterEncoding,";= ")+_contentType.substring(i2);
+                        }
+                        _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
+                    }
+                }
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletResponse#setContentLength(int)
+     */
+    public void setContentLength(int len)
+    {
+        // Protect from setting after committed as default handling
+        // of a servlet HEAD request ALWAYS sets _content length, even
+        // if the getHandling committed the response!
+        if (isCommitted() || _connection.isIncluding())
+            return;
+        _connection._generator.setContentLength(len);
+        if (len>0)
+        {
+            _connection.getResponseFields().putLongField(HttpHeaders.CONTENT_LENGTH, len);
+            if (_connection._generator.isAllContentWritten())
+            {
+                if (_outputState==WRITER)
+                    _writer.close();
+                else if (_outputState==STREAM)
+                {
+                    try
+                    {
+                        getOutputStream().close();
+                    }
+                    catch(IOException e)
+                    {
+                        throw new RuntimeException(e);
+                    }
+                }
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletResponse#setContentLength(int)
+     */
+    public void setLongContentLength(long len)
+    {
+        // Protect from setting after committed as default handling
+        // of a servlet HEAD request ALWAYS sets _content length, even
+        // if the getHandling committed the response!
+        if (isCommitted() || _connection.isIncluding())
+        	return;
+        _connection._generator.setContentLength(len);
+        _connection.getResponseFields().putLongField(HttpHeaders.CONTENT_LENGTH, len);
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletResponse#setContentType(java.lang.String)
+     */
+    public void setContentType(String contentType)
+    {
+        if (isCommitted() || _connection.isIncluding())
+            return;
+
+        // Yes this method is horribly complex.... but there are lots of special cases and
+        // as this method is called on every request, it is worth trying to save string creation.
+        //
+
+        if (contentType==null)
+        {
+            if (_locale==null)
+                _characterEncoding=null;
+            _mimeType=null;
+            _cachedMimeType=null;
+            _contentType=null;
+            _connection.getResponseFields().remove(HttpHeaders.CONTENT_TYPE_BUFFER);
+        }
+        else
+        {
+            // Look for encoding in contentType
+            int i0=contentType.indexOf(';');
+
+            if (i0>0)
+            {
+                // we have content type parameters
+
+                // Extract params off mimetype
+                _mimeType=contentType.substring(0,i0).trim();
+                _cachedMimeType=MimeTypes.CACHE.get(_mimeType);
+
+                // Look for charset
+                int i1=contentType.indexOf("charset=",i0+1);
+                if (i1>=0)
+                {
+                    _explicitEncoding=true;
+                    int i8=i1+8;
+                    int i2 = contentType.indexOf(' ',i8);
+
+                    if (_outputState==WRITER)
+                    {
+                        // strip the charset and ignore;
+                        if ((i1==i0+1 && i2<0) || (i1==i0+2 && i2<0 && contentType.charAt(i0+1)==' '))
+                        {
+                            if (_cachedMimeType!=null)
+                            {
+                                CachedBuffer content_type = _cachedMimeType.getAssociate(_characterEncoding);
+                                if (content_type!=null)
+                                {
+                                    _contentType=content_type.toString();
+                                    _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,content_type);
+                                }
+                                else
+                                {
+                                    _contentType=_mimeType+";charset="+_characterEncoding;
+                                    _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
+                                }
+                            }
+                            else
+                            {
+                                _contentType=_mimeType+";charset="+_characterEncoding;
+                                _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
+                            }
+                        }
+                        else if (i2<0)
+                        {
+                            _contentType=contentType.substring(0,i1)+";charset="+QuotedStringTokenizer.quoteIfNeeded(_characterEncoding,";= ");
+                            _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
+                        }
+                        else
+                        {
+                            _contentType=contentType.substring(0,i1)+contentType.substring(i2)+";charset="+QuotedStringTokenizer.quoteIfNeeded(_characterEncoding,";= ");
+                            _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
+                        }
+                    }
+                    else if ((i1==i0+1 && i2<0) || (i1==i0+2 && i2<0 && contentType.charAt(i0+1)==' '))
+                    {
+                        // The params are just the char encoding
+                        _cachedMimeType=MimeTypes.CACHE.get(_mimeType);
+                        _characterEncoding = QuotedStringTokenizer.unquote(contentType.substring(i8));
+
+                        if (_cachedMimeType!=null)
+                        {
+                            CachedBuffer content_type = _cachedMimeType.getAssociate(_characterEncoding);
+                            if (content_type!=null)
+                            {
+                                _contentType=content_type.toString();
+                                _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,content_type);
+                            }
+                            else
+                            {
+                                _contentType=contentType;
+                                _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
+                            }
+                        }
+                        else
+                        {
+                            _contentType=contentType;
+                            _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
+                        }
+                    }
+                    else if (i2>0)
+                    {
+                        _characterEncoding = QuotedStringTokenizer.unquote(contentType.substring(i8,i2));
+                        _contentType=contentType;
+                        _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
+                    }
+                    else
+                    {
+                        _characterEncoding = QuotedStringTokenizer.unquote(contentType.substring(i8));
+                        _contentType=contentType;
+                        _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
+                    }
+                }
+                else // No encoding in the params.
+                {
+                    _cachedMimeType=null;
+                    _contentType=_characterEncoding==null?contentType:contentType+";charset="+QuotedStringTokenizer.quoteIfNeeded(_characterEncoding,";= ");
+                    _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
+                }
+            }
+            else // No params at all
+            {
+                _mimeType=contentType;
+                _cachedMimeType=MimeTypes.CACHE.get(_mimeType);
+
+                if (_characterEncoding!=null)
+                {
+                    if (_cachedMimeType!=null)
+                    {
+                        CachedBuffer content_type = _cachedMimeType.getAssociate(_characterEncoding);
+                        if (content_type!=null)
+                        {
+                            _contentType=content_type.toString();
+                            _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,content_type);
+                        }
+                        else
+                        {
+                            _contentType=_mimeType+";charset="+QuotedStringTokenizer.quoteIfNeeded(_characterEncoding,";= ");
+                            _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
+                        }
+                    }
+                    else
+                    {
+                        _contentType=contentType+";charset="+QuotedStringTokenizer.quoteIfNeeded(_characterEncoding,";= ");
+                        _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
+                    }
+                }
+                else if (_cachedMimeType!=null)
+                {
+                    _contentType=_cachedMimeType.toString();
+                    _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_cachedMimeType);
+                }
+                else
+                {
+                    _contentType=contentType;
+                    _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
+                }
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletResponse#setBufferSize(int)
+     */
+    public void setBufferSize(int size)
+    {
+        if (isCommitted() || getContentCount()>0)
+            throw new IllegalStateException("Committed or content written");
+        _connection.getGenerator().increaseContentBufferSize(size);
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletResponse#getBufferSize()
+     */
+    public int getBufferSize()
+    {
+        return _connection.getGenerator().getContentBufferSize();
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletResponse#flushBuffer()
+     */
+    public void flushBuffer() throws IOException
+    {
+        _connection.flushResponse();
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletResponse#reset()
+     */
+    public void reset()
+    {
+        resetBuffer();
+        fwdReset();
+        _status=200;
+        _reason=null;
+        
+        HttpFields response_fields=_connection.getResponseFields();
+        
+        response_fields.clear();
+        String connection=_connection.getRequestFields().getStringField(HttpHeaders.CONNECTION_BUFFER);
+        if (connection!=null)
+        {
+            String[] values = connection.split(",");
+            for  (int i=0;values!=null && i<values.length;i++)
+            {
+                CachedBuffer cb = HttpHeaderValues.CACHE.get(values[0].trim());
+
+                if (cb!=null)
+                {
+                    switch(cb.getOrdinal())
+                    {
+                        case HttpHeaderValues.CLOSE_ORDINAL:
+                            response_fields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.CLOSE_BUFFER);
+                            break;
+
+                        case HttpHeaderValues.KEEP_ALIVE_ORDINAL:
+                            if (HttpVersions.HTTP_1_0.equalsIgnoreCase(_connection.getRequest().getProtocol()))
+                                response_fields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.KEEP_ALIVE);
+                            break;
+                        case HttpHeaderValues.TE_ORDINAL:
+                            response_fields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.TE);
+                            break;
+                    }
+                }
+            }
+        }
+    }
+    
+
+    public void reset(boolean preserveCookies)
+    { 
+        if (!preserveCookies)
+            reset();
+        else
+        {
+            HttpFields response_fields=_connection.getResponseFields();
+
+            ArrayList<String> cookieValues = new ArrayList<String>(5);
+            Enumeration<String> vals = response_fields.getValues(HttpHeaders.SET_COOKIE);
+            while (vals.hasMoreElements())
+                cookieValues.add((String)vals.nextElement());
+
+            reset();
+
+            for (String v:cookieValues)
+                response_fields.add(HttpHeaders.SET_COOKIE, v);
+        }
+    }
+    
+    
+    
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletResponse#reset()
+     */
+    public void fwdReset()
+    {
+        resetBuffer();
+
+        _writer=null;
+        _outputState=NONE;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletResponse#resetBuffer()
+     */
+    public void resetBuffer()
+    {
+        if (isCommitted())
+            throw new IllegalStateException("Committed");
+        _connection.getGenerator().resetBuffer();
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletResponse#isCommitted()
+     */
+    public boolean isCommitted()
+    {
+        return _connection.isResponseCommitted();
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletResponse#setLocale(java.util.Locale)
+     */
+    public void setLocale(Locale locale)
+    {
+        if (locale == null || isCommitted() ||_connection.isIncluding())
+            return;
+
+        _locale = locale;
+        _connection.getResponseFields().put(HttpHeaders.CONTENT_LANGUAGE_BUFFER,locale.toString().replace('_','-'));
+
+        if (_explicitEncoding || _outputState!=0 )
+            return;
+
+        if (_connection.getRequest().getContext()==null)
+            return;
+
+        String charset = _connection.getRequest().getContext().getContextHandler().getLocaleEncoding(locale);
+
+        if (charset!=null && charset.length()>0)
+        {
+            _characterEncoding=charset;
+
+            /* get current MIME type from Content-Type header */
+            String type=getContentType();
+            if (type!=null)
+            {
+                _characterEncoding=charset;
+                int semi=type.indexOf(';');
+                if (semi<0)
+                {
+                    _mimeType=type;
+                    _contentType= type += ";charset="+charset;
+                }
+                else
+                {
+                    _mimeType=type.substring(0,semi);
+                    _contentType= _mimeType += ";charset="+charset;
+                }
+
+                _cachedMimeType=MimeTypes.CACHE.get(_mimeType);
+                _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType);
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.ServletResponse#getLocale()
+     */
+    public Locale getLocale()
+    {
+        if (_locale==null)
+            return Locale.getDefault();
+        return _locale;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The HTTP status code that has been set for this request. This will be <code>200<code>
+     *    ({@link HttpServletResponse#SC_OK}), unless explicitly set through one of the <code>setStatus</code> methods.
+     */
+    public int getStatus()
+    {
+        return _status;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The reason associated with the current {@link #getStatus() status}. This will be <code>null</code>,
+     *    unless one of the <code>setStatus</code> methods have been called.
+     */
+    public String getReason()
+    {
+        return _reason;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     */
+    public void complete()
+        throws IOException
+    {
+        _connection.completeResponse();
+    }
+
+    /* ------------------------------------------------------------- */
+    /**
+     * @return the number of bytes actually written in response body
+     */
+    public long getContentCount()
+    {
+        if (_connection==null || _connection.getGenerator()==null)
+            return -1;
+        return _connection.getGenerator().getContentWritten();
+    }
+
+    /* ------------------------------------------------------------ */
+    public HttpFields getHttpFields()
+    {
+        return _connection.getResponseFields();
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public String toString()
+    {
+        return "HTTP/1.1 "+_status+" "+ (_reason==null?"":_reason) +System.getProperty("line.separator")+
+        _connection.getResponseFields().toString();
+    }
+    
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    private static class NullOutput extends ServletOutputStream
+    {
+        @Override
+        public void write(int b) throws IOException
+        {
+        }
+
+        @Override
+        public void print(String s) throws IOException
+        {
+        }
+
+        @Override
+        public void println(String s) throws IOException
+        {
+        }
+
+        @Override
+        public void write(byte[] b, int off, int len) throws IOException
+        {
+        }
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/Server.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,662 @@
+//
+//  ========================================================================
+//  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.server;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.util.Enumeration;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.HttpGenerator;
+import org.eclipse.jetty.http.HttpURI;
+import org.eclipse.jetty.server.handler.HandlerWrapper;
+import org.eclipse.jetty.server.nio.SelectChannelConnector;
+import org.eclipse.jetty.util.Attributes;
+import org.eclipse.jetty.util.AttributesMap;
+import org.eclipse.jetty.util.LazyList;
+import org.eclipse.jetty.util.MultiException;
+import org.eclipse.jetty.util.TypeUtil;
+import org.eclipse.jetty.util.URIUtil;
+import org.eclipse.jetty.util.component.Container;
+import org.eclipse.jetty.util.component.Destroyable;
+import org.eclipse.jetty.util.component.LifeCycle;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.eclipse.jetty.util.thread.ShutdownThread;
+import org.eclipse.jetty.util.thread.ThreadPool;
+
+/* ------------------------------------------------------------ */
+/** Jetty HTTP Servlet Server.
+ * This class is the main class for the Jetty HTTP Servlet server.
+ * It aggregates Connectors (HTTP request receivers) and request Handlers.
+ * The server is itself a handler and a ThreadPool.  Connectors use the ThreadPool methods
+ * to run jobs that will eventually call the handle method.
+ *
+ *  @org.apache.xbean.XBean  description="Creates an embedded Jetty web server"
+ */
+public class Server extends HandlerWrapper implements Attributes
+{
+    private static final Logger LOG = Log.getLogger(Server.class);
+
+    private static final String __version;
+    static
+    {
+        if (Server.class.getPackage()!=null &&
+            "Eclipse.org - Jetty".equals(Server.class.getPackage().getImplementationVendor()) &&
+             Server.class.getPackage().getImplementationVersion()!=null)
+            __version=Server.class.getPackage().getImplementationVersion();
+        else
+            __version=System.getProperty("jetty.version","8.y.z-SNAPSHOT");
+    }
+
+    private final Container _container=new Container();
+    private final AttributesMap _attributes = new AttributesMap();
+    private ThreadPool _threadPool;
+    private Connector[] _connectors;
+    private SessionIdManager _sessionIdManager;
+    private boolean _sendServerVersion = true; //send Server: header
+    private boolean _sendDateHeader = false; //send Date: header
+    private int _graceful=0;
+    private boolean _stopAtShutdown;
+    private boolean _dumpAfterStart=false;
+    private boolean _dumpBeforeStop=false;
+    private boolean _uncheckedPrintWriter=false;
+
+
+    /* ------------------------------------------------------------ */
+    public Server()
+    {
+        setServer(this);
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Convenience constructor
+     * Creates server and a {@link SelectChannelConnector} at the passed port.
+     */
+    public Server(int port)
+    {
+        setServer(this);
+
+        Connector connector=new SelectChannelConnector();
+        connector.setPort(port);
+        setConnectors(new Connector[]{connector});
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Convenience constructor
+     * Creates server and a {@link SelectChannelConnector} at the passed address.
+     */
+    public Server(InetSocketAddress addr)
+    {
+        setServer(this);
+
+        Connector connector=new SelectChannelConnector();
+        connector.setHost(addr.getHostName());
+        connector.setPort(addr.getPort());
+        setConnectors(new Connector[]{connector});
+    }
+
+
+    /* ------------------------------------------------------------ */
+    public static String getVersion()
+    {
+        return __version;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the container.
+     */
+    public Container getContainer()
+    {
+        return _container;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean getStopAtShutdown()
+    {
+        return _stopAtShutdown;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setStopAtShutdown(boolean stop)
+    {
+        //if we now want to stop
+        if (stop)
+        {
+            //and we weren't stopping before
+            if (!_stopAtShutdown)
+            {  
+                //only register to stop if we're already started (otherwise we'll do it in doStart())
+                if (isStarted()) 
+                    ShutdownThread.register(this);
+            }
+        }
+        else
+            ShutdownThread.deregister(this);
+        
+        _stopAtShutdown=stop;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the connectors.
+     */
+    public Connector[] getConnectors()
+    {
+        return _connectors;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void addConnector(Connector connector)
+    {
+        setConnectors((Connector[])LazyList.addToArray(getConnectors(), connector, Connector.class));
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Conveniance method which calls {@link #getConnectors()} and {@link #setConnectors(Connector[])} to
+     * remove a connector.
+     * @param connector The connector to remove.
+     */
+    public void removeConnector(Connector connector) {
+        setConnectors((Connector[])LazyList.removeFromArray (getConnectors(), connector));
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set the connectors for this server.
+     * Each connector has this server set as it's ThreadPool and its Handler.
+     * @param connectors The connectors to set.
+     */
+    public void setConnectors(Connector[] connectors)
+    {
+        if (connectors!=null)
+        {
+            for (int i=0;i<connectors.length;i++)
+                connectors[i].setServer(this);
+        }
+
+        _container.update(this, _connectors, connectors, "connector");
+        _connectors = connectors;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the threadPool.
+     */
+    public ThreadPool getThreadPool()
+    {
+        return _threadPool;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param threadPool The threadPool to set.
+     */
+    public void setThreadPool(ThreadPool threadPool)
+    {
+        if (_threadPool!=null)
+            removeBean(_threadPool);
+        _container.update(this, _threadPool, threadPool, "threadpool",false);
+        _threadPool = threadPool;
+        if (_threadPool!=null)
+            addBean(_threadPool);
+    }
+
+    /**
+     * @return true if {@link #dumpStdErr()} is called after starting
+     */
+    public boolean isDumpAfterStart()
+    {
+        return _dumpAfterStart;
+    }
+
+    /**
+     * @param dumpAfterStart true if {@link #dumpStdErr()} is called after starting
+     */
+    public void setDumpAfterStart(boolean dumpAfterStart)
+    {
+        _dumpAfterStart = dumpAfterStart;
+    }
+
+    /**
+     * @return true if {@link #dumpStdErr()} is called before stopping
+     */
+    public boolean isDumpBeforeStop()
+    {
+        return _dumpBeforeStop;
+    }
+
+    /**
+     * @param dumpBeforeStop true if {@link #dumpStdErr()} is called before stopping
+     */
+    public void setDumpBeforeStop(boolean dumpBeforeStop)
+    {
+        _dumpBeforeStop = dumpBeforeStop;
+    }
+
+
+
+    /* ------------------------------------------------------------ */
+    @Override
+    protected void doStart() throws Exception
+    {
+        if (getStopAtShutdown())
+        {
+            ShutdownThread.register(this);    
+        }
+        
+        ShutdownMonitor.getInstance().start(); // initialize
+
+        LOG.info("jetty-"+__version);
+        HttpGenerator.setServerVersion(__version);
+        
+        MultiException mex=new MultiException();
+
+        if (_threadPool==null)
+            setThreadPool(new QueuedThreadPool());
+
+        try
+        {
+            super.doStart();
+        }
+        catch(Throwable e)
+        {
+            mex.add(e);
+        }
+
+        if (_connectors!=null && mex.size()==0)
+        {
+            for (int i=0;i<_connectors.length;i++)
+            {
+                try{_connectors[i].start();}
+                catch(Throwable e)
+                {
+                    mex.add(e);
+                }
+            }
+        }
+
+        if (isDumpAfterStart())
+            dumpStdErr();
+
+        mex.ifExceptionThrow();
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    protected void doStop() throws Exception
+    {
+        if (isDumpBeforeStop())
+            dumpStdErr();
+
+        MultiException mex=new MultiException();
+
+        if (_graceful>0)
+        {
+            if (_connectors!=null)
+            {
+                for (int i=_connectors.length;i-->0;)
+                {
+                    LOG.info("Graceful shutdown {}",_connectors[i]);
+                    try{_connectors[i].close();}catch(Throwable e){mex.add(e);}
+                }
+            }
+
+            Handler[] contexts = getChildHandlersByClass(Graceful.class);
+            for (int c=0;c<contexts.length;c++)
+            {
+                Graceful context=(Graceful)contexts[c];
+                LOG.info("Graceful shutdown {}",context);
+                context.setShutdown(true);
+            }
+            Thread.sleep(_graceful);
+        }
+
+        if (_connectors!=null)
+        {
+            for (int i=_connectors.length;i-->0;)
+                try{_connectors[i].stop();}catch(Throwable e){mex.add(e);}
+        }
+
+        try {super.doStop(); } catch(Throwable e) { mex.add(e);}
+
+        mex.ifExceptionThrow();
+
+        if (getStopAtShutdown())
+            ShutdownThread.deregister(this);
+    }
+
+    /* ------------------------------------------------------------ */
+    /* Handle a request from a connection.
+     * Called to handle a request on the connection when either the header has been received,
+     * or after the entire request has been received (for short requests of known length), or
+     * on the dispatch of an async request.
+     */
+    public void handle(AbstractHttpConnection connection) throws IOException, ServletException
+    {
+        final String target=connection.getRequest().getPathInfo();
+        final Request request=connection.getRequest();
+        final Response response=connection.getResponse();
+
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("REQUEST "+target+" on "+connection);
+            handle(target, request, request, response);
+            LOG.debug("RESPONSE "+target+"  "+connection.getResponse().getStatus()+" handled="+request.isHandled());
+        }
+        else
+            handle(target, request, request, response);
+    }
+
+    /* ------------------------------------------------------------ */
+    /* Handle a request from a connection.
+     * Called to handle a request on the connection when either the header has been received,
+     * or after the entire request has been received (for short requests of known length), or
+     * on the dispatch of an async request.
+     */
+    public void handleAsync(AbstractHttpConnection connection) throws IOException, ServletException
+    {
+        final AsyncContinuation async = connection.getRequest().getAsyncContinuation();
+        final AsyncContinuation.AsyncEventState state = async.getAsyncEventState();
+
+        final Request baseRequest=connection.getRequest();
+        final String path=state.getPath();
+
+        if (path!=null)
+        {
+            // this is a dispatch with a path
+            final String contextPath=state.getServletContext().getContextPath();
+            HttpURI uri = new HttpURI(URIUtil.addPaths(contextPath,path));
+            baseRequest.setUri(uri);
+            baseRequest.setRequestURI(null);
+            baseRequest.setPathInfo(baseRequest.getRequestURI());
+            if (uri.getQuery()!=null)
+                baseRequest.mergeQueryString(uri.getQuery()); //we have to assume dispatch path and query are UTF8
+        }
+
+        final String target=baseRequest.getPathInfo();
+        final HttpServletRequest request=(HttpServletRequest)async.getRequest();
+        final HttpServletResponse response=(HttpServletResponse)async.getResponse();
+
+        if (LOG.isDebugEnabled())
+        {
+            LOG.debug("REQUEST "+target+" on "+connection);
+            handle(target, baseRequest, request, response);
+            LOG.debug("RESPONSE "+target+"  "+connection.getResponse().getStatus());
+        }
+        else
+            handle(target, baseRequest, request, response);
+
+    }
+
+
+    /* ------------------------------------------------------------ */
+    public void join() throws InterruptedException
+    {
+        getThreadPool().join();
+    }
+
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the sessionIdManager.
+     */
+    public SessionIdManager getSessionIdManager()
+    {
+        return _sessionIdManager;
+    }
+
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    /**
+     * @param sessionIdManager The sessionIdManager to set.
+     */
+    public void setSessionIdManager(SessionIdManager sessionIdManager)
+    {
+        if (_sessionIdManager!=null)
+            removeBean(_sessionIdManager);
+        _container.update(this, _sessionIdManager, sessionIdManager, "sessionIdManager",false);
+        _sessionIdManager = sessionIdManager;
+        if (_sessionIdManager!=null)
+            addBean(_sessionIdManager);
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setSendServerVersion (boolean sendServerVersion)
+    {
+        _sendServerVersion = sendServerVersion;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean getSendServerVersion()
+    {
+        return _sendServerVersion;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param sendDateHeader
+     */
+    public void setSendDateHeader(boolean sendDateHeader)
+    {
+        _sendDateHeader = sendDateHeader;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean getSendDateHeader()
+    {
+        return _sendDateHeader;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** 
+     */
+    @Deprecated
+    public int getMaxCookieVersion()
+    {
+        return 1;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** 
+     */
+    @Deprecated
+    public void setMaxCookieVersion(int maxCookieVersion)
+    {
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Add a LifeCycle object to be started/stopped
+     * along with the Server.
+     * @deprecated Use {@link #addBean(Object)}
+     * @param c
+     */
+    @Deprecated
+    public void addLifeCycle (LifeCycle c)
+    {
+        addBean(c);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Add an associated bean.
+     * The bean will be added to the servers {@link Container}
+     * and if it is a {@link LifeCycle} instance, it will be
+     * started/stopped along with the Server. Any beans that are also
+     * {@link Destroyable}, will be destroyed with the server.
+     * @param o the bean object to add
+     */
+    @Override
+    public boolean addBean(Object o)
+    {
+        if (super.addBean(o))
+        {
+            _container.addBean(o);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Remove a LifeCycle object to be started/stopped
+     * along with the Server
+     * @deprecated Use {@link #removeBean(Object)}
+     */
+    @Deprecated
+    public void removeLifeCycle (LifeCycle c)
+    {
+        removeBean(c);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Remove an associated bean.
+     */
+    @Override
+    public boolean removeBean (Object o)
+    {
+        if (super.removeBean(o))
+        {
+            _container.removeBean(o);
+            return true;
+        }
+        return false;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.util.AttributesMap#clearAttributes()
+     */
+    public void clearAttributes()
+    {
+        _attributes.clearAttributes();
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.util.AttributesMap#getAttribute(java.lang.String)
+     */
+    public Object getAttribute(String name)
+    {
+        return _attributes.getAttribute(name);
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.util.AttributesMap#getAttributeNames()
+     */
+    public Enumeration getAttributeNames()
+    {
+        return AttributesMap.getAttributeNamesCopy(_attributes);
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.util.AttributesMap#removeAttribute(java.lang.String)
+     */
+    public void removeAttribute(String name)
+    {
+        _attributes.removeAttribute(name);
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.util.AttributesMap#setAttribute(java.lang.String, java.lang.Object)
+     */
+    public void setAttribute(String name, Object attribute)
+    {
+        _attributes.setAttribute(name, attribute);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return the graceful
+     */
+    public int getGracefulShutdown()
+    {
+        return _graceful;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set graceful shutdown timeout.  If set, the internal <code>doStop()</code> method will not immediately stop the
+     * server. Instead, all {@link Connector}s will be closed so that new connections will not be accepted
+     * and all handlers that implement {@link Graceful} will be put into the shutdown mode so that no new requests
+     * will be accepted, but existing requests can complete.  The server will then wait the configured timeout
+     * before stopping.
+     * @param timeoutMS the milliseconds to wait for existing request to complete before stopping the server.
+     *
+     */
+    public void setGracefulShutdown(int timeoutMS)
+    {
+        _graceful=timeoutMS;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public String toString()
+    {
+        return this.getClass().getName()+"@"+Integer.toHexString(hashCode());
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void dump(Appendable out,String indent) throws IOException
+    {
+        dumpThis(out);
+        dump(out,indent,TypeUtil.asList(getHandlers()),getBeans(),TypeUtil.asList(_connectors));
+    }
+
+
+    /* ------------------------------------------------------------ */
+    public boolean isUncheckedPrintWriter()
+    {
+        return _uncheckedPrintWriter;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setUncheckedPrintWriter(boolean unchecked)
+    {
+        _uncheckedPrintWriter=unchecked;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /* A handler that can be gracefully shutdown.
+     * Called by doStop if a {@link #setGracefulShutdown} period is set.
+     * TODO move this somewhere better
+     */
+    public interface Graceful extends Handler
+    {
+        public void setShutdown(boolean shutdown);
+    }
+
+    /* ------------------------------------------------------------ */
+    public static void main(String...args) throws Exception
+    {
+        System.err.println(getVersion());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/ServletRequestHttpWrapper.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,212 @@
+//
+//  ========================================================================
+//  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.server;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.Collection;
+import java.util.Enumeration;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletRequestWrapper;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.Part;
+
+/* ------------------------------------------------------------ */
+/** Class to tunnel a ServletRequest via a HttpServletRequest
+ */
+public class ServletRequestHttpWrapper extends ServletRequestWrapper implements HttpServletRequest
+{
+    public ServletRequestHttpWrapper(ServletRequest request)
+    {
+        super(request);
+    }
+
+    public String getAuthType()
+    {
+        return null;
+    }
+
+    public Cookie[] getCookies()
+    {
+        return null;
+    }
+
+    public long getDateHeader(String name)
+    {
+        return 0;
+    }
+
+    public String getHeader(String name)
+    {
+        return null;
+    }
+
+    public Enumeration getHeaders(String name)
+    {
+        return null;
+    }
+
+    public Enumeration getHeaderNames()
+    {
+        return null;
+    }
+
+    public int getIntHeader(String name)
+    {
+        return 0;
+    }
+
+    public String getMethod()
+    {
+        return null;
+    }
+
+    public String getPathInfo()
+    {
+        return null;
+    }
+
+    public String getPathTranslated()
+    {
+        return null;
+    }
+
+    public String getContextPath()
+    {
+        return null;
+    }
+
+    public String getQueryString()
+    {
+        return null;
+    }
+
+    public String getRemoteUser()
+    {
+        return null;
+    }
+
+    public boolean isUserInRole(String role)
+    {
+        return false;
+    }
+
+    public Principal getUserPrincipal()
+    {
+        return null;
+    }
+
+    public String getRequestedSessionId()
+    {
+        return null;
+    }
+
+    public String getRequestURI()
+    {
+        return null;
+    }
+
+    public StringBuffer getRequestURL()
+    {
+        return null;
+    }
+
+    public String getServletPath()
+    {
+        return null;
+    }
+
+    public HttpSession getSession(boolean create)
+    {
+        return null;
+    }
+
+    public HttpSession getSession()
+    {
+        return null;
+    }
+
+    public boolean isRequestedSessionIdValid()
+    {
+        return false;
+    }
+
+    public boolean isRequestedSessionIdFromCookie()
+    {
+        return false;
+    }
+
+    public boolean isRequestedSessionIdFromURL()
+    {
+        return false;
+    }
+
+    public boolean isRequestedSessionIdFromUrl()
+    {
+        return false;
+    }
+
+    /** 
+     * @see javax.servlet.http.HttpServletRequest#authenticate(javax.servlet.http.HttpServletResponse)
+     */
+    public boolean authenticate(HttpServletResponse response) throws IOException, ServletException
+    {
+        return false;
+    }
+
+    /** 
+     * @see javax.servlet.http.HttpServletRequest#getPart(java.lang.String)
+     */
+    public Part getPart(String name) throws IOException, ServletException
+    {
+        return null;
+    }
+
+    /** 
+     * @see javax.servlet.http.HttpServletRequest#getParts()
+     */
+    public Collection<Part> getParts() throws IOException, ServletException
+    {
+        return null;
+    }
+
+    /** 
+     * @see javax.servlet.http.HttpServletRequest#login(java.lang.String, java.lang.String)
+     */
+    public void login(String username, String password) throws ServletException
+    {
+
+    }
+
+    /** 
+     * @see javax.servlet.http.HttpServletRequest#logout()
+     */
+    public void logout() throws ServletException
+    {
+        
+    }
+
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/ServletResponseHttpWrapper.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,145 @@
+//
+//  ========================================================================
+//  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.server;
+
+import java.io.IOException;
+import java.util.Collection;
+
+import javax.servlet.ServletResponse;
+import javax.servlet.ServletResponseWrapper;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+
+
+/* ------------------------------------------------------------ */
+/** Wrapper to tunnel a ServletResponse via a HttpServletResponse
+ */
+public class ServletResponseHttpWrapper extends ServletResponseWrapper implements HttpServletResponse
+{
+    public ServletResponseHttpWrapper(ServletResponse response)
+    {
+        super(response);
+    }
+
+    public void addCookie(Cookie cookie)
+    {        
+    }
+
+    public boolean containsHeader(String name)
+    {
+        return false;
+    }
+
+    public String encodeURL(String url)
+    {
+        return null;
+    }
+
+    public String encodeRedirectURL(String url)
+    {
+        return null;
+    }
+
+    public String encodeUrl(String url)
+    {
+        return null;
+    }
+
+    public String encodeRedirectUrl(String url)
+    {
+        return null;
+    }
+
+    public void sendError(int sc, String msg) throws IOException
+    {        
+    }
+
+    public void sendError(int sc) throws IOException
+    {        
+    }
+
+    public void sendRedirect(String location) throws IOException
+    {        
+    }
+
+    public void setDateHeader(String name, long date)
+    {        
+    }
+
+    public void addDateHeader(String name, long date)
+    {        
+    }
+
+    public void setHeader(String name, String value)
+    {        
+    }
+
+    public void addHeader(String name, String value)
+    {        
+    }
+
+    public void setIntHeader(String name, int value)
+    {        
+    }
+
+    public void addIntHeader(String name, int value)
+    {        
+    }
+
+    public void setStatus(int sc)
+    {        
+    }
+
+    public void setStatus(int sc, String sm)
+    {        
+    }
+
+    /** 
+     * @see javax.servlet.http.HttpServletResponse#getHeader(java.lang.String)
+     */
+    public String getHeader(String name)
+    {
+        return null;
+    }
+
+    /** 
+     * @see javax.servlet.http.HttpServletResponse#getHeaderNames()
+     */
+    public Collection<String> getHeaderNames()
+    {
+        return null;
+    }
+
+    /** 
+     * @see javax.servlet.http.HttpServletResponse#getHeaders(java.lang.String)
+     */
+    public Collection<String> getHeaders(String name)
+    {
+        return null;
+    }
+
+    /** 
+     * @see javax.servlet.http.HttpServletResponse#getStatus()
+     */
+    public int getStatus()
+    {
+        return 0;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/SessionIdManager.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,81 @@
+//
+//  ========================================================================
+//  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.server;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+import org.eclipse.jetty.util.component.LifeCycle;
+
+/** Session ID Manager.
+ * Manages session IDs across multiple contexts.
+ */
+public interface SessionIdManager extends LifeCycle
+{
+    /**
+     * @param id The session ID without any cluster node extension
+     * @return True if the session ID is in use by at least one context.
+     */
+    public boolean idInUse(String id);
+    
+    /**
+     * Add a session to the list of known sessions for a given ID.
+     * @param session The session
+     */
+    public void addSession(HttpSession session);
+    
+    /**
+     * Remove session from the list of known sessions for a given ID.
+     * @param session
+     */
+    public void removeSession(HttpSession session);
+    
+    /**
+     * Call {@link HttpSession#invalidate()} on all known sessions for the given id.
+     * @param id The session ID without any cluster node extension
+     */
+    public void invalidateAll(String id);
+    
+    /**
+     * @param request
+     * @param created
+     * @return the new session id
+     */
+    public String newSessionId(HttpServletRequest request,long created);
+    
+    public String getWorkerName();
+    
+    
+    /* ------------------------------------------------------------ */
+    /** Get a cluster ID from a node ID.
+     * Strip node identifier from a located session ID.
+     * @param nodeId
+     * @return the cluster id
+     */
+    public String getClusterId(String nodeId);
+    
+    /* ------------------------------------------------------------ */
+    /** Get a node ID from a cluster ID and a request
+     * @param clusterId The ID of the session
+     * @param request The request that for the session (or null)
+     * @return The session ID qualified with the node ID.
+     */
+    public String getNodeId(String clusterId,HttpServletRequest request);
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/SessionManager.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,307 @@
+//
+//  ========================================================================
+//  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.server;
+
+import java.util.EventListener;
+import java.util.Set;
+
+import javax.servlet.SessionCookieConfig;
+import javax.servlet.SessionTrackingMode;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+import org.eclipse.jetty.http.HttpCookie;
+import org.eclipse.jetty.server.session.SessionHandler;
+import org.eclipse.jetty.util.component.LifeCycle;
+
+/* --------------------------------------------------------------------- */
+/**
+ * Session Manager.
+ * The API required to manage sessions for a servlet context.
+ *
+ */
+
+/* ------------------------------------------------------------ */
+/**
+ */
+public interface SessionManager extends LifeCycle
+{
+    /* ------------------------------------------------------------ */
+    /**
+     * Session cookie name.
+     * Defaults to <code>JSESSIONID</code>, but can be set with the
+     * <code>org.eclipse.jetty.servlet.SessionCookie</code> context init parameter.
+     */
+    public final static String __SessionCookieProperty = "org.eclipse.jetty.servlet.SessionCookie";
+    public final static String __DefaultSessionCookie = "JSESSIONID";
+
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Session id path parameter name.
+     * Defaults to <code>jsessionid</code>, but can be set with the
+     * <code>org.eclipse.jetty.servlet.SessionIdPathParameterName</code> context init parameter.
+     * If set to null or "none" no URL rewriting will be done.
+     */
+    public final static String __SessionIdPathParameterNameProperty = "org.eclipse.jetty.servlet.SessionIdPathParameterName";
+    public final static String __DefaultSessionIdPathParameterName = "jsessionid";
+    public final static String __CheckRemoteSessionEncoding = "org.eclipse.jetty.servlet.CheckingRemoteSessionIdEncoding";
+
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Session Domain.
+     * If this property is set as a ServletContext InitParam, then it is
+     * used as the domain for session cookies. If it is not set, then
+     * no domain is specified for the session cookie.
+     */
+    public final static String __SessionDomainProperty = "org.eclipse.jetty.servlet.SessionDomain";
+    public final static String __DefaultSessionDomain = null;
+
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Session Path.
+     * If this property is set as a ServletContext InitParam, then it is
+     * used as the path for the session cookie.  If it is not set, then
+     * the context path is used as the path for the cookie.
+     */
+    public final static String __SessionPathProperty = "org.eclipse.jetty.servlet.SessionPath";
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Session Max Age.
+     * If this property is set as a ServletContext InitParam, then it is
+     * used as the max age for the session cookie.  If it is not set, then
+     * a max age of -1 is used.
+     */
+    public final static String __MaxAgeProperty = "org.eclipse.jetty.servlet.MaxAge";
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns the <code>HttpSession</code> with the given session id
+     *
+     * @param id the session id
+     * @return the <code>HttpSession</code> with the corresponding id or null if no session with the given id exists
+     */
+    public HttpSession getHttpSession(String id);
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Creates a new <code>HttpSession</code>.
+     *
+     * @param request the HttpServletRequest containing the requested session id
+     * @return the new <code>HttpSession</code>
+     */
+    public HttpSession newHttpSession(HttpServletRequest request);
+
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return true if session cookies should be HTTP-only (Microsoft extension)
+     * @see org.eclipse.jetty.http.HttpCookie#isHttpOnly()
+     */
+    public boolean getHttpOnly();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return the max period of inactivity, after which the session is invalidated, in seconds.
+     * @see #setMaxInactiveInterval(int)
+     */
+    public int getMaxInactiveInterval();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Sets the max period of inactivity, after which the session is invalidated, in seconds.
+     *
+     * @param seconds the max inactivity period, in seconds.
+     * @see #getMaxInactiveInterval()
+     */
+    public void setMaxInactiveInterval(int seconds);
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Sets the {@link SessionHandler}.
+     *
+     * @param handler the <code>SessionHandler</code> object
+     */
+    public void setSessionHandler(SessionHandler handler);
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Adds an event listener for session-related events.
+     *
+     * @param listener the session event listener to add
+     *                 Individual SessionManagers implementations may accept arbitrary listener types,
+     *                 but they are expected to at least handle HttpSessionActivationListener,
+     *                 HttpSessionAttributeListener, HttpSessionBindingListener and HttpSessionListener.
+     * @see #removeEventListener(EventListener)
+     */
+    public void addEventListener(EventListener listener);
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Removes an event listener for for session-related events.
+     *
+     * @param listener the session event listener to remove
+     * @see #addEventListener(EventListener)
+     */
+    public void removeEventListener(EventListener listener);
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Removes all event listeners for session-related events.
+     *
+     * @see #removeEventListener(EventListener)
+     */
+    public void clearEventListeners();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Gets a Cookie for a session.
+     *
+     * @param session         the session to which the cookie should refer.
+     * @param contextPath     the context to which the cookie should be linked.
+     *                        The client will only send the cookie value when requesting resources under this path.
+     * @param requestIsSecure whether the client is accessing the server over a secure protocol (i.e. HTTPS).
+     * @return if this <code>SessionManager</code> uses cookies, then this method will return a new
+     *         {@link Cookie cookie object} that should be set on the client in order to link future HTTP requests
+     *         with the <code>session</code>. If cookies are not in use, this method returns <code>null</code>.
+     */
+    public HttpCookie getSessionCookie(HttpSession session, String contextPath, boolean requestIsSecure);
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return the cross context session id manager.
+     * @see #setSessionIdManager(SessionIdManager)
+     */
+    public SessionIdManager getSessionIdManager();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return the cross context session id manager.
+     * @deprecated use {@link #getSessionIdManager()}
+     */
+    @Deprecated
+    public SessionIdManager getMetaManager();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Sets the cross context session id manager
+     *
+     * @param idManager the cross context session id manager.
+     * @see #getSessionIdManager()
+     */
+    public void setSessionIdManager(SessionIdManager idManager);
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param session the session to test for validity
+     * @return whether the given session is valid, that is, it has not been invalidated.
+     */
+    public boolean isValid(HttpSession session);
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param session the session object
+     * @return the unique id of the session within the cluster, extended with an optional node id.
+     * @see #getClusterId(HttpSession)
+     */
+    public String getNodeId(HttpSession session);
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param session the session object
+     * @return the unique id of the session within the cluster (without a node id extension)
+     * @see #getNodeId(HttpSession)
+     */
+    public String getClusterId(HttpSession session);
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Called by the {@link SessionHandler} when a session is first accessed by a request.
+     *
+     * @param session the session object
+     * @param secure  whether the request is secure or not
+     * @return the session cookie. If not null, this cookie should be set on the response to either migrate
+     *         the session or to refresh a session cookie that may expire.
+     * @see #complete(HttpSession)
+     */
+    public HttpCookie access(HttpSession session, boolean secure);
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Called by the {@link SessionHandler} when a session is last accessed by a request.
+     *
+     * @param session the session object
+     * @see #access(HttpSession, boolean)
+     */
+    public void complete(HttpSession session);
+
+    /**
+     * Sets the session id URL path parameter name.
+     *
+     * @param parameterName the URL path parameter name for session id URL rewriting (null or "none" for no rewriting).
+     * @see #getSessionIdPathParameterName()
+     * @see #getSessionIdPathParameterNamePrefix()
+     */
+    public void setSessionIdPathParameterName(String parameterName);
+
+    /**
+     * @return the URL path parameter name for session id URL rewriting, by default "jsessionid".
+     * @see #setSessionIdPathParameterName(String)
+     */
+    public String getSessionIdPathParameterName();
+
+    /**
+     * @return a formatted version of {@link #getSessionIdPathParameterName()}, by default
+     *         ";" + sessionIdParameterName + "=", for easier lookup in URL strings.
+     * @see #getSessionIdPathParameterName()
+     */
+    public String getSessionIdPathParameterNamePrefix();
+
+    /**
+     * @return whether the session management is handled via cookies.
+     */
+    public boolean isUsingCookies();
+    
+    /**
+     * @return whether the session management is handled via URLs.
+     */
+    public boolean isUsingURLs();
+
+    public Set<SessionTrackingMode> getDefaultSessionTrackingModes();
+
+    public Set<SessionTrackingMode> getEffectiveSessionTrackingModes();
+
+    public void setSessionTrackingModes(Set<SessionTrackingMode> sessionTrackingModes);
+
+    public SessionCookieConfig getSessionCookieConfig();
+    
+    /**
+     * @return True if absolute URLs are check for remoteness before being session encoded.
+     */
+    public boolean isCheckingRemoteSessionIdEncoding();
+    
+    /**
+     * @param remote True if absolute URLs are check for remoteness before being session encoded.
+     */
+    public void setCheckingRemoteSessionIdEncoding(boolean remote);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/ShutdownMonitor.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,384 @@
+//
+//  ========================================================================
+//  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.server;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.Properties;
+
+import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.thread.ShutdownThread;
+
+/**
+ * Shutdown/Stop Monitor thread.
+ * <p>
+ * This thread listens on the port specified by the STOP.PORT system parameter (defaults to -1 for not listening) for request authenticated with the key given
+ * by the STOP.KEY system parameter (defaults to "eclipse") for admin requests.
+ * <p>
+ * If the stop port is set to zero, then a random port is assigned and the port number is printed to stdout.
+ * <p>
+ * Commands "stop" and "status" are currently supported.
+ */
+public class ShutdownMonitor 
+{
+    // Implementation of safe lazy init, using Initialization on Demand Holder technique.
+    static class Holder
+    {
+        static ShutdownMonitor instance = new ShutdownMonitor();
+    }
+
+    public static ShutdownMonitor getInstance()
+    {
+        return Holder.instance;
+    }
+
+    /**
+     * ShutdownMonitorThread
+     *
+     * Thread for listening to STOP.PORT for command to stop Jetty.
+     * If ShowndownMonitor.exitVm is true, then Sytem.exit will also be
+     * called after the stop.
+     *
+     */
+    public class ShutdownMonitorThread extends Thread
+    {
+
+        public ShutdownMonitorThread ()
+        {
+            setDaemon(true);
+            setName("ShutdownMonitor");
+        }
+        
+        @Override
+        public void run()
+        {
+            if (serverSocket == null)
+            {
+                return;
+            }
+
+            while (serverSocket != null)
+            {
+                Socket socket = null;
+                try
+                {
+                    socket = serverSocket.accept();
+
+                    LineNumberReader lin = new LineNumberReader(new InputStreamReader(socket.getInputStream()));
+                    String receivedKey = lin.readLine();
+                    if (!key.equals(receivedKey))
+                    {
+                        System.err.println("Ignoring command with incorrect key");
+                        continue;
+                    }
+
+                    OutputStream out = socket.getOutputStream();
+
+                    String cmd = lin.readLine();
+                    debug("command=%s",cmd);
+                    if ("stop".equals(cmd))
+                    {
+                        // Graceful Shutdown
+                        debug("Issuing graceful shutdown..");
+                        ShutdownThread.getInstance().run();
+
+                        // Reply to client
+                        debug("Informing client that we are stopped.");
+                        out.write("Stopped\r\n".getBytes(StringUtil.__UTF8));
+                        out.flush();
+
+                        // Shutdown Monitor
+                        debug("Shutting down monitor");
+                        close(socket);
+                        socket = null;
+                        close(serverSocket);
+                        serverSocket = null;
+
+                        if (exitVm)
+                        {
+                            // Kill JVM
+                            debug("Killing JVM");
+                            System.exit(0);
+                        }
+                    }
+                    else if ("status".equals(cmd))
+                    {
+                        // Reply to client
+                        out.write("OK\r\n".getBytes(StringUtil.__UTF8));
+                        out.flush();
+                    }
+                }
+                catch (Exception e)
+                {
+                    debug(e);
+                    System.err.println(e.toString());
+                }
+                finally
+                {
+                    close(socket);
+                    socket = null;
+                }
+            }
+        }
+        
+        public void start()
+        {
+            if (isAlive())
+            {
+                System.err.printf("ShutdownMonitorThread already started");
+                return; // cannot start it again
+            }
+
+            startListenSocket();
+            
+            if (serverSocket == null)
+            {
+                return;
+            }
+            if (DEBUG)
+                System.err.println("Starting ShutdownMonitorThread");
+            super.start();
+        }
+        
+        private void startListenSocket()
+        {
+            if (port < 0)
+            {            
+                if (DEBUG)
+                    System.err.println("ShutdownMonitor not in use (port < 0): " + port);
+                return;
+            }
+
+            try
+            {
+                serverSocket = new ServerSocket(port,1,InetAddress.getByName("127.0.0.1"));
+                if (port == 0)
+                {
+                    // server assigned port in use
+                    port = serverSocket.getLocalPort();
+                    System.out.printf("STOP.PORT=%d%n",port);
+                }
+
+                if (key == null)
+                {
+                    // create random key
+                    key = Long.toString((long)(Long.MAX_VALUE * Math.random() + this.hashCode() + System.currentTimeMillis()),36);
+                    System.out.printf("STOP.KEY=%s%n",key);
+                }
+            }
+            catch (Exception e)
+            {
+                debug(e);
+                System.err.println("Error binding monitor port " + port + ": " + e.toString());
+                serverSocket = null;
+            }
+            finally
+            {
+                // establish the port and key that are in use
+                debug("STOP.PORT=%d",port);
+                debug("STOP.KEY=%s",key);
+                debug("%s",serverSocket);
+            }
+        }
+
+    }
+    
+    private boolean DEBUG;
+    private int port;
+    private String key;
+    private boolean exitVm;
+    private ServerSocket serverSocket;
+    private ShutdownMonitorThread thread;
+    
+    
+
+    /**
+     * Create a ShutdownMonitor using configuration from the System properties.
+     * <p>
+     * <code>STOP.PORT</code> = the port to listen on (empty, null, or values less than 0 disable the stop ability)<br>
+     * <code>STOP.KEY</code> = the magic key/passphrase to allow the stop (defaults to "eclipse")<br>
+     * <p>
+     * Note: server socket will only listen on localhost, and a successful stop will issue a System.exit() call.
+     */
+    private ShutdownMonitor()
+    {
+        Properties props = System.getProperties();
+
+        this.DEBUG = props.containsKey("DEBUG");
+
+        // Use values passed thru via /jetty-start/
+        this.port = Integer.parseInt(props.getProperty("STOP.PORT","-1"));
+        this.key = props.getProperty("STOP.KEY",null);
+        this.exitVm = true;
+    }
+
+    private void close(ServerSocket server)
+    {
+        if (server == null)
+        {
+            return;
+        }
+
+        try
+        {
+            server.close();
+        }
+        catch (IOException ignore)
+        {
+            /* ignore */
+        }
+    }
+
+    private void close(Socket socket)
+    {
+        if (socket == null)
+        {
+            return;
+        }
+
+        try
+        {
+            socket.close();
+        }
+        catch (IOException ignore)
+        {
+            /* ignore */
+        }
+    }
+
+    private void debug(String format, Object... args)
+    {
+        if (DEBUG)
+        {
+            System.err.printf("[ShutdownMonitor] " + format + "%n",args);
+        }
+    }
+
+    private void debug(Throwable t)
+    {
+        if (DEBUG)
+        {
+            t.printStackTrace(System.err);
+        }
+    }
+
+    public String getKey()
+    {
+        return key;
+    }
+
+    public int getPort()
+    {
+        return port;
+    }
+
+    public ServerSocket getServerSocket()
+    {
+        return serverSocket;
+    }
+
+    public boolean isExitVm()
+    {
+        return exitVm;
+    }
+
+
+    public void setDebug(boolean flag)
+    {
+        this.DEBUG = flag;
+    }
+
+    public void setExitVm(boolean exitVm)
+    {
+        synchronized (this)
+        {
+            if (thread != null && thread.isAlive())
+            {
+                throw new IllegalStateException("ShutdownMonitorThread already started");
+            }
+            this.exitVm = exitVm;
+        }
+    }
+
+    public void setKey(String key)
+    {
+        synchronized (this)
+        {
+            if (thread != null && thread.isAlive())
+            {
+                throw new IllegalStateException("ShutdownMonitorThread already started");
+            }
+            this.key = key;
+        }
+    }
+
+    public void setPort(int port)
+    {
+        synchronized (this)
+        {
+            if (thread != null && thread.isAlive())
+            {
+                throw new IllegalStateException("ShutdownMonitorThread already started");
+            }
+            this.port = port;
+        }
+    }
+
+    protected void start() throws Exception
+    {
+        ShutdownMonitorThread t = null;
+        synchronized (this)
+        {
+            if (thread != null && thread.isAlive())
+            {
+                System.err.printf("ShutdownMonitorThread already started");
+                return; // cannot start it again
+            }
+         
+            thread = new ShutdownMonitorThread();
+            t = thread;
+        }
+         
+        if (t != null)
+            t.start();
+    }
+
+
+    protected boolean isAlive ()
+    {
+        boolean result = false;
+        synchronized (this)
+        {
+            result = (thread != null && thread.isAlive());
+        }
+        return result;
+    }
+    
+ 
+    @Override
+    public String toString()
+    {
+        return String.format("%s[port=%d]",this.getClass().getName(),port);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/UserIdentity.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,116 @@
+//
+//  ========================================================================
+//  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.server;
+import java.security.Principal;
+import java.util.Map;
+
+import javax.security.auth.Subject;
+
+/* ------------------------------------------------------------ */
+/** User object that encapsulates user identity and operations such as run-as-role actions, 
+ * checking isUserInRole and getUserPrincipal.
+ *
+ * Implementations of UserIdentity should be immutable so that they may be
+ * cached by Authenticators and LoginServices.
+ *
+ */
+public interface UserIdentity
+{
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The user subject
+     */
+    Subject getSubject();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The user principal
+     */
+    Principal getUserPrincipal();
+
+    /* ------------------------------------------------------------ */
+    /** Check if the user is in a role.
+     * This call is used to satisfy authorization calls from 
+     * container code which will be using translated role names.
+     * @param role A role name.
+     * @param scope
+     * @return True if the user can act in that role.
+     */
+    boolean isUserInRole(String role, Scope scope);
+    
+
+    /* ------------------------------------------------------------ */
+    /**
+     * A UserIdentity Scope.
+     * A scope is the environment in which a User Identity is to 
+     * be interpreted. Typically it is set by the target servlet of 
+     * a request.
+     */
+    interface Scope
+    {
+        /* ------------------------------------------------------------ */
+        /**
+         * @return The context path that the identity is being considered within
+         */
+        String getContextPath();
+        
+        /* ------------------------------------------------------------ */
+        /**
+         * @return The name of the identity context. Typically this is the servlet name.
+         */
+        String getName();
+        
+        /* ------------------------------------------------------------ */
+        /**
+         * @return A map of role reference names that converts from names used by application code
+         * to names used by the context deployment.
+         */
+        Map<String,String> getRoleRefMap();
+    }
+    
+    /* ------------------------------------------------------------ */
+    public interface UnauthenticatedUserIdentity extends UserIdentity
+    {
+    }
+
+    /* ------------------------------------------------------------ */
+    public static final UserIdentity UNAUTHENTICATED_IDENTITY = new UnauthenticatedUserIdentity()
+    {
+        public Subject getSubject()
+        {
+            return null;
+        }
+        
+        public Principal getUserPrincipal()
+        {
+            return null;
+        }
+        
+        public boolean isUserInRole(String role, Scope scope)
+        {
+            return false;
+        }
+        
+        @Override
+        public String toString()
+        {
+            return "UNAUTHENTICATED";
+        }
+    };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/bio/SocketConnector.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,325 @@
+//
+//  ========================================================================
+//  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.server.bio;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketException;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.jetty.http.HttpException;
+import org.eclipse.jetty.io.Buffer;
+import org.eclipse.jetty.io.ConnectedEndPoint;
+import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.EofException;
+import org.eclipse.jetty.io.bio.SocketEndPoint;
+import org.eclipse.jetty.server.AbstractConnector;
+import org.eclipse.jetty.server.AbstractHttpConnection;
+import org.eclipse.jetty.server.BlockingHttpConnection;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.util.component.AggregateLifeCycle;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+
+/* ------------------------------------------------------------------------------- */
+/**  Socket Connector.
+ * This connector implements a traditional blocking IO and threading model.
+ * Normal JRE sockets are used and a thread is allocated per connection.
+ * Buffers are managed so that large buffers are only allocated to active connections.
+ *
+ * This Connector should only be used if NIO is not available.
+ *
+ * @org.apache.xbean.XBean element="bioConnector" description="Creates a BIO based socket connector"
+ *
+ *
+ */
+public class SocketConnector extends AbstractConnector
+{
+    private static final Logger LOG = Log.getLogger(SocketConnector.class);
+
+    protected ServerSocket _serverSocket;
+    protected final Set<EndPoint> _connections;
+    protected volatile int _localPort=-1;
+
+    /* ------------------------------------------------------------ */
+    /** Constructor.
+     *
+     */
+    public SocketConnector()
+    {
+        _connections=new HashSet<EndPoint>();
+    }
+
+    /* ------------------------------------------------------------ */
+    public Object getConnection()
+    {
+        return _serverSocket;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void open() throws IOException
+    {
+        // Create a new server socket and set to non blocking mode
+        if (_serverSocket==null || _serverSocket.isClosed())
+        _serverSocket= newServerSocket(getHost(),getPort(),getAcceptQueueSize());
+        _serverSocket.setReuseAddress(getReuseAddress());
+        _localPort=_serverSocket.getLocalPort();
+        if (_localPort<=0)
+            throw new IllegalStateException("port not allocated for "+this);
+
+    }
+
+    /* ------------------------------------------------------------ */
+    protected ServerSocket newServerSocket(String host, int port,int backlog) throws IOException
+    {
+        ServerSocket ss= host==null?
+            new ServerSocket(port,backlog):
+            new ServerSocket(port,backlog,InetAddress.getByName(host));
+
+        return ss;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void close() throws IOException
+    {
+        if (_serverSocket!=null)
+            _serverSocket.close();
+        _serverSocket=null;
+        _localPort=-2;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void accept(int acceptorID)
+    	throws IOException, InterruptedException
+    {
+        Socket socket = _serverSocket.accept();
+        configure(socket);
+
+        ConnectorEndPoint connection=new ConnectorEndPoint(socket);
+        connection.dispatch();
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    /**
+     * Allows subclass to override Conection if required.
+     */
+    protected Connection newConnection(EndPoint endpoint)
+    {
+        return new BlockingHttpConnection(this, endpoint, getServer());
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    @Override
+    public void customize(EndPoint endpoint, Request request)
+        throws IOException
+    {
+        ConnectorEndPoint connection = (ConnectorEndPoint)endpoint;
+        int lrmit = isLowResources()?_lowResourceMaxIdleTime:_maxIdleTime;
+        connection.setMaxIdleTime(lrmit);
+
+        super.customize(endpoint, request);
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    public int getLocalPort()
+    {
+        return _localPort;
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    @Override
+    protected void doStart() throws Exception
+    {
+        _connections.clear();
+        super.doStart();
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    @Override
+    protected void doStop() throws Exception
+    {
+        super.doStop();
+        Set<EndPoint> set = new HashSet<EndPoint>();
+        synchronized(_connections)
+        {
+            set.addAll(_connections);
+        }
+        for (EndPoint endPoint : set)
+        {
+            ConnectorEndPoint connection = (ConnectorEndPoint)endPoint;
+            connection.close();
+        }
+    }
+
+    @Override
+    public void dump(Appendable out, String indent) throws IOException
+    {
+        super.dump(out, indent);
+        Set<EndPoint> connections = new HashSet<EndPoint>();
+        synchronized (_connections)
+        {
+            connections.addAll(_connections);
+        }
+        AggregateLifeCycle.dump(out, indent, connections);
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    /* ------------------------------------------------------------------------------- */
+    /* ------------------------------------------------------------------------------- */
+    protected class ConnectorEndPoint extends SocketEndPoint implements Runnable, ConnectedEndPoint
+    {
+        volatile Connection _connection;
+        protected final Socket _socket;
+
+        public ConnectorEndPoint(Socket socket) throws IOException
+        {
+            super(socket,_maxIdleTime);
+            _connection = newConnection(this);
+            _socket=socket;
+        }
+
+        public Connection getConnection()
+        {
+            return _connection;
+        }
+
+        public void setConnection(Connection connection)
+        {
+            if (_connection!=connection && _connection!=null)
+                connectionUpgraded(_connection,connection);
+            _connection=connection;
+        }
+
+        public void dispatch() throws IOException
+        {
+            if (getThreadPool()==null || !getThreadPool().dispatch(this))
+            {
+                LOG.warn("dispatch failed for {}",_connection);
+                close();
+            }
+        }
+
+        @Override
+        public int fill(Buffer buffer) throws IOException
+        {
+            int l = super.fill(buffer);
+            if (l<0)
+            {
+                if (!isInputShutdown())
+                    shutdownInput();
+                if (isOutputShutdown())
+                    close();
+            }
+            return l;
+        }
+
+        @Override
+        public void close() throws IOException
+        {
+            if (_connection instanceof AbstractHttpConnection)
+                ((AbstractHttpConnection)_connection).getRequest().getAsyncContinuation().cancel();
+            super.close();
+        }
+
+        public void run()
+        {
+            try
+            {
+                connectionOpened(_connection);
+                synchronized(_connections)
+                {
+                    _connections.add(this);
+                }
+
+                while (isStarted() && !isClosed())
+                {
+                    if (_connection.isIdle())
+                    {
+                        if (isLowResources())
+                            setMaxIdleTime(getLowResourcesMaxIdleTime());
+                    }
+
+                    _connection=_connection.handle();
+                }
+            }
+            catch (EofException e)
+            {
+                LOG.debug("EOF", e);
+                try{close();}
+                catch(IOException e2){LOG.ignore(e2);}
+            }
+            catch (SocketException e)
+            {
+                LOG.debug("EOF", e);
+                try{close();}
+                catch(IOException e2){LOG.ignore(e2);}
+            }
+            catch (HttpException e)
+            {
+                LOG.debug("BAD", e);
+                try{close();}
+                catch(IOException e2){LOG.ignore(e2);}
+            }
+            catch(Exception e)
+            {
+                LOG.warn("handle failed?",e);
+                try{close();}
+                catch(IOException e2){LOG.ignore(e2);}
+            }
+            finally
+            {
+                connectionClosed(_connection);
+                synchronized(_connections)
+                {
+                    _connections.remove(this);
+                }
+
+                // wait for client to close, but if not, close ourselves.
+                try
+                {
+                    if (!_socket.isClosed())
+                    {
+                        long timestamp=System.currentTimeMillis();
+                        int max_idle=getMaxIdleTime();
+
+                        _socket.setSoTimeout(getMaxIdleTime());
+                        int c=0;
+                        do
+                        {
+                            c = _socket.getInputStream().read();
+                        }
+                        while (c>=0 && (System.currentTimeMillis()-timestamp)<max_idle);
+                        if (!_socket.isClosed())
+                            _socket.close();
+                    }
+                }
+                catch(IOException e)
+                {
+                    LOG.ignore(e);
+                }
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/handler/AbstractHandler.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,105 @@
+//
+//  ========================================================================
+//  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.server.handler;
+
+
+import java.io.IOException;
+
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.util.component.AggregateLifeCycle;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+
+/* ------------------------------------------------------------ */
+/** AbstractHandler.
+ * 
+ *
+ */
+public abstract class AbstractHandler extends AggregateLifeCycle implements Handler
+{
+    private static final Logger LOG = Log.getLogger(AbstractHandler.class);
+
+    private Server _server;
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * 
+     */
+    public AbstractHandler()
+    {
+    }
+
+    /* ------------------------------------------------------------ */
+    /* 
+     * @see org.eclipse.thread.LifeCycle#start()
+     */
+    @Override
+    protected void doStart() throws Exception
+    {
+        LOG.debug("starting {}",this);
+        super.doStart();
+    }
+
+    /* ------------------------------------------------------------ */
+    /* 
+     * @see org.eclipse.thread.LifeCycle#stop()
+     */
+    @Override
+    protected void doStop() throws Exception
+    {
+        LOG.debug("stopping {}",this);
+        super.doStop();
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setServer(Server server)
+    {
+        Server old_server=_server;
+        if (old_server!=null && old_server!=server)
+            old_server.getContainer().removeBean(this);
+        _server=server;
+        if (_server!=null && _server!=old_server)
+            _server.getContainer().addBean(this);
+    }
+
+    /* ------------------------------------------------------------ */
+    public Server getServer()
+    {
+        return _server;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void destroy()
+    {
+        if (!isStopped())
+            throw new IllegalStateException("!STOPPED");
+        super.destroy();
+        if (_server!=null)
+            _server.getContainer().removeBean(this);
+    }
+
+    /* ------------------------------------------------------------ */
+    public void dumpThis(Appendable out) throws IOException
+    {
+        out.append(toString()).append(" - ").append(getState()).append('\n');
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/handler/AbstractHandlerContainer.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,123 @@
+//
+//  ========================================================================
+//  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.server.handler;
+
+
+import java.io.IOException;
+
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.HandlerContainer;
+import org.eclipse.jetty.util.LazyList;
+import org.eclipse.jetty.util.TypeUtil;
+
+
+/* ------------------------------------------------------------ */
+/** Abstract Handler Container.
+ * This is the base class for handlers that may contain other handlers.
+ *
+ */
+public abstract class AbstractHandlerContainer extends AbstractHandler implements HandlerContainer
+{
+    /* ------------------------------------------------------------ */
+    public AbstractHandlerContainer()
+    {
+    }
+    
+    /* ------------------------------------------------------------ */
+    public Handler[] getChildHandlers()
+    {
+        Object list = expandChildren(null,null);
+        return (Handler[])LazyList.toArray(list, Handler.class);
+    }
+        
+    /* ------------------------------------------------------------ */
+    public Handler[] getChildHandlersByClass(Class<?> byclass)
+    {
+        Object list = expandChildren(null,byclass);
+        return (Handler[])LazyList.toArray(list, byclass);
+    }
+    
+    /* ------------------------------------------------------------ */
+    public <T extends Handler> T getChildHandlerByClass(Class<T> byclass)
+    {
+        // TODO this can be more efficient?
+        Object list = expandChildren(null,byclass);
+        if (list==null)
+            return null;
+        return (T)LazyList.get(list, 0);
+    }
+    
+    /* ------------------------------------------------------------ */
+    protected Object expandChildren(Object list, Class<?> byClass)
+    {
+        return list;
+    }
+
+    /* ------------------------------------------------------------ */
+    protected Object expandHandler(Handler handler, Object list, Class<Handler> byClass)
+    {
+        if (handler==null)
+            return list;
+        
+        if (byClass==null || byClass.isAssignableFrom(handler.getClass()))
+            list=LazyList.add(list, handler);
+
+        if (handler instanceof AbstractHandlerContainer)
+            list=((AbstractHandlerContainer)handler).expandChildren(list, byClass);
+        else if (handler instanceof HandlerContainer)
+        {
+            HandlerContainer container = (HandlerContainer)handler;
+            Handler[] handlers=byClass==null?container.getChildHandlers():container.getChildHandlersByClass(byClass);
+            list=LazyList.addArray(list, handlers);
+        }
+        
+        return list;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public static <T extends HandlerContainer> T findContainerOf(HandlerContainer root,Class<T>type, Handler handler)
+    {
+        if (root==null || handler==null)
+            return null;
+        
+        Handler[] branches=root.getChildHandlersByClass(type);
+        if (branches!=null)
+        {
+            for (Handler h:branches)
+            {
+                T container = (T)h;
+                Handler[] candidates = container.getChildHandlersByClass(handler.getClass());
+                if (candidates!=null)
+                {
+                    for (Handler c:candidates)
+                        if (c==handler)
+                            return container;
+                }
+            }
+        }
+        return null;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public void dump(Appendable out,String indent) throws IOException
+    {
+        dumpThis(out);
+        dump(out,indent,getBeans(),TypeUtil.asList(getHandlers()));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/handler/ConnectHandler.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,1025 @@
+//
+//  ========================================================================
+//  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.server.handler;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.SocketException;
+import java.net.SocketTimeoutException;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.SocketChannel;
+import java.util.Arrays;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.HttpMethods;
+import org.eclipse.jetty.http.HttpParser;
+import org.eclipse.jetty.io.AsyncEndPoint;
+import org.eclipse.jetty.io.Buffer;
+import org.eclipse.jetty.io.ConnectedEndPoint;
+import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.nio.AsyncConnection;
+import org.eclipse.jetty.io.nio.IndirectNIOBuffer;
+import org.eclipse.jetty.io.nio.SelectChannelEndPoint;
+import org.eclipse.jetty.io.nio.SelectorManager;
+import org.eclipse.jetty.server.AbstractHttpConnection;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.util.HostMap;
+import org.eclipse.jetty.util.TypeUtil;
+import org.eclipse.jetty.util.component.LifeCycle;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.ThreadPool;
+
+/**
+ * <p>Implementation of a tunneling proxy that supports HTTP CONNECT.</p>
+ * <p>To work as CONNECT proxy, objects of this class must be instantiated using the no-arguments
+ * constructor, since the remote server information will be present in the CONNECT URI.</p>
+ */
+public class ConnectHandler extends HandlerWrapper
+{
+    private static final Logger LOG = Log.getLogger(ConnectHandler.class);
+    private final SelectorManager _selectorManager = new Manager();
+    private volatile int _connectTimeout = 5000;
+    private volatile int _writeTimeout = 30000;
+    private volatile ThreadPool _threadPool;
+    private volatile boolean _privateThreadPool;
+    private HostMap<String> _white = new HostMap<String>();
+    private HostMap<String> _black = new HostMap<String>();
+
+    public ConnectHandler()
+    {
+        this(null);
+    }
+
+    public ConnectHandler(String[] white, String[] black)
+    {
+        this(null, white, black);
+    }
+
+    public ConnectHandler(Handler handler)
+    {
+        setHandler(handler);
+    }
+
+    public ConnectHandler(Handler handler, String[] white, String[] black)
+    {
+        setHandler(handler);
+        set(white, _white);
+        set(black, _black);
+    }
+
+    /**
+     * @return the timeout, in milliseconds, to connect to the remote server
+     */
+    public int getConnectTimeout()
+    {
+        return _connectTimeout;
+    }
+
+    /**
+     * @param connectTimeout the timeout, in milliseconds, to connect to the remote server
+     */
+    public void setConnectTimeout(int connectTimeout)
+    {
+        _connectTimeout = connectTimeout;
+    }
+
+    /**
+     * @return the timeout, in milliseconds, to write data to a peer
+     */
+    public int getWriteTimeout()
+    {
+        return _writeTimeout;
+    }
+
+    /**
+     * @param writeTimeout the timeout, in milliseconds, to write data to a peer
+     */
+    public void setWriteTimeout(int writeTimeout)
+    {
+        _writeTimeout = writeTimeout;
+    }
+
+    @Override
+    public void setServer(Server server)
+    {
+        super.setServer(server);
+
+        server.getContainer().update(this, null, _selectorManager, "selectManager");
+
+        if (_privateThreadPool)
+            server.getContainer().update(this, null, _privateThreadPool, "threadpool", true);
+        else
+            _threadPool = server.getThreadPool();
+    }
+
+    /**
+     * @return the thread pool
+     */
+    public ThreadPool getThreadPool()
+    {
+        return _threadPool;
+    }
+
+    /**
+     * @param threadPool the thread pool
+     */
+    public void setThreadPool(ThreadPool threadPool)
+    {
+        if (getServer() != null)
+            getServer().getContainer().update(this, _privateThreadPool ? _threadPool : null, threadPool, "threadpool", true);
+        _privateThreadPool = threadPool != null;
+        _threadPool = threadPool;
+    }
+
+    @Override
+    protected void doStart() throws Exception
+    {
+        super.doStart();
+
+        if (_threadPool == null)
+        {
+            _threadPool = getServer().getThreadPool();
+            _privateThreadPool = false;
+        }
+        if (_threadPool instanceof LifeCycle && !((LifeCycle)_threadPool).isRunning())
+            ((LifeCycle)_threadPool).start();
+
+        _selectorManager.start();
+    }
+
+    @Override
+    protected void doStop() throws Exception
+    {
+        _selectorManager.stop();
+
+        ThreadPool threadPool = _threadPool;
+        if (_privateThreadPool && _threadPool != null && threadPool instanceof LifeCycle)
+            ((LifeCycle)threadPool).stop();
+
+        super.doStop();
+    }
+
+    @Override
+    public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+    {
+        if (HttpMethods.CONNECT.equalsIgnoreCase(request.getMethod()))
+        {
+            LOG.debug("CONNECT request for {}", request.getRequestURI());
+            try
+            {
+                handleConnect(baseRequest, request, response, request.getRequestURI());
+            }
+            catch(Exception e)
+            {
+                LOG.warn("ConnectHandler "+baseRequest.getUri()+" "+ e);
+                LOG.debug(e);
+            }
+        }
+        else
+        {
+            super.handle(target, baseRequest, request, response);
+        }
+    }
+
+    /**
+     * <p>Handles a CONNECT request.</p>
+     * <p>CONNECT requests may have authentication headers such as <code>Proxy-Authorization</code>
+     * that authenticate the client with the proxy.</p>
+     *
+     * @param baseRequest   Jetty-specific http request
+     * @param request       the http request
+     * @param response      the http response
+     * @param serverAddress the remote server address in the form {@code host:port}
+     * @throws ServletException if an application error occurs
+     * @throws IOException      if an I/O error occurs
+     */
+    protected void handleConnect(Request baseRequest, HttpServletRequest request, HttpServletResponse response, String serverAddress) throws ServletException, IOException
+    {
+        boolean proceed = handleAuthentication(request, response, serverAddress);
+        if (!proceed)
+            return;
+
+        String host = serverAddress;
+        int port = 80;
+        int colon = serverAddress.indexOf(':');
+        if (colon > 0)
+        {
+            host = serverAddress.substring(0, colon);
+            port = Integer.parseInt(serverAddress.substring(colon + 1));
+        }
+
+        if (!validateDestination(host))
+        {
+            LOG.info("ProxyHandler: Forbidden destination " + host);
+            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
+            baseRequest.setHandled(true);
+            return;
+        }
+
+        SocketChannel channel;
+
+        try
+        {
+            channel = connectToServer(request,host,port);
+        }
+        catch (SocketException se)
+        {
+            LOG.info("ConnectHandler: SocketException " + se.getMessage());
+            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+            baseRequest.setHandled(true);
+            return;
+        }
+        catch (SocketTimeoutException ste)
+        {
+            LOG.info("ConnectHandler: SocketTimeoutException" + ste.getMessage());
+            response.setStatus(HttpServletResponse.SC_GATEWAY_TIMEOUT);
+            baseRequest.setHandled(true);
+            return;
+        }
+        catch (IOException ioe)
+        {
+            LOG.info("ConnectHandler: IOException" + ioe.getMessage());
+            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+            baseRequest.setHandled(true);
+            return;
+        }
+
+        // Transfer unread data from old connection to new connection
+        // We need to copy the data to avoid races:
+        // 1. when this unread data is written and the server replies before the clientToProxy
+        // connection is installed (it is only installed after returning from this method)
+        // 2. when the client sends data before this unread data has been written.
+        AbstractHttpConnection httpConnection = AbstractHttpConnection.getCurrentConnection();
+        Buffer headerBuffer = ((HttpParser)httpConnection.getParser()).getHeaderBuffer();
+        Buffer bodyBuffer = ((HttpParser)httpConnection.getParser()).getBodyBuffer();
+        int length = headerBuffer == null ? 0 : headerBuffer.length();
+        length += bodyBuffer == null ? 0 : bodyBuffer.length();
+        IndirectNIOBuffer buffer = null;
+        if (length > 0)
+        {
+            buffer = new IndirectNIOBuffer(length);
+            if (headerBuffer != null)
+            {
+                buffer.put(headerBuffer);
+                headerBuffer.clear();
+            }
+            if (bodyBuffer != null)
+            {
+                buffer.put(bodyBuffer);
+                bodyBuffer.clear();
+            }
+        }
+
+        ConcurrentMap<String, Object> context = new ConcurrentHashMap<String, Object>();
+        prepareContext(request, context);
+
+        ClientToProxyConnection clientToProxy = prepareConnections(context, channel, buffer);
+
+        // CONNECT expects a 200 response
+        response.setStatus(HttpServletResponse.SC_OK);
+
+        // Prevent close
+        baseRequest.getConnection().getGenerator().setPersistent(true);
+
+        // Close to force last flush it so that the client receives it
+        response.getOutputStream().close();
+
+        upgradeConnection(request, response, clientToProxy);
+    }
+
+    private ClientToProxyConnection prepareConnections(ConcurrentMap<String, Object> context, SocketChannel channel, Buffer buffer)
+    {
+        AbstractHttpConnection httpConnection = AbstractHttpConnection.getCurrentConnection();
+        ProxyToServerConnection proxyToServer = newProxyToServerConnection(context, buffer);
+        ClientToProxyConnection clientToProxy = newClientToProxyConnection(context, channel, httpConnection.getEndPoint(), httpConnection.getTimeStamp());
+        clientToProxy.setConnection(proxyToServer);
+        proxyToServer.setConnection(clientToProxy);
+        return clientToProxy;
+    }
+
+    /**
+     * <p>Handles the authentication before setting up the tunnel to the remote server.</p>
+     * <p>The default implementation returns true.</p>
+     *
+     * @param request  the HTTP request
+     * @param response the HTTP response
+     * @param address  the address of the remote server in the form {@code host:port}.
+     * @return true to allow to connect to the remote host, false otherwise
+     * @throws ServletException to report a server error to the caller
+     * @throws IOException      to report a server error to the caller
+     */
+    protected boolean handleAuthentication(HttpServletRequest request, HttpServletResponse response, String address) throws ServletException, IOException
+    {
+        return true;
+    }
+
+    protected ClientToProxyConnection newClientToProxyConnection(ConcurrentMap<String, Object> context, SocketChannel channel, EndPoint endPoint, long timeStamp)
+    {
+        return new ClientToProxyConnection(context, channel, endPoint, timeStamp);
+    }
+
+    protected ProxyToServerConnection newProxyToServerConnection(ConcurrentMap<String, Object> context, Buffer buffer)
+    {
+        return new ProxyToServerConnection(context, buffer);
+    }
+
+    // may return null
+    private SocketChannel connectToServer(HttpServletRequest request, String host, int port) throws IOException
+    {
+        SocketChannel channel = connect(request, host, port);      
+        channel.configureBlocking(false);
+        return channel;
+    }
+
+    /**
+     * <p>Establishes a connection to the remote server.</p>
+     *
+     * @param request the HTTP request that initiated the tunnel
+     * @param host    the host to connect to
+     * @param port    the port to connect to
+     * @return a {@link SocketChannel} connected to the remote server
+     * @throws IOException if the connection cannot be established
+     */
+    protected SocketChannel connect(HttpServletRequest request, String host, int port) throws IOException
+    {
+        SocketChannel channel = SocketChannel.open();
+
+        if (channel == null)
+        {
+            throw new IOException("unable to connect to " + host + ":" + port);
+        }
+
+        try
+        {
+            // Connect to remote server
+            LOG.debug("Establishing connection to {}:{}", host, port);
+            channel.socket().setTcpNoDelay(true);
+            channel.socket().connect(new InetSocketAddress(host, port), getConnectTimeout());
+            LOG.debug("Established connection to {}:{}", host, port);
+            return channel;
+        }
+        catch (IOException x)
+        {
+            LOG.debug("Failed to establish connection to " + host + ":" + port, x);
+            try
+            {
+                channel.close();
+            }
+            catch (IOException xx)
+            {
+                LOG.ignore(xx);
+            }
+            throw x;
+        }
+    }
+
+    protected void prepareContext(HttpServletRequest request, ConcurrentMap<String, Object> context)
+    {
+    }
+
+    private void upgradeConnection(HttpServletRequest request, HttpServletResponse response, Connection connection) throws IOException
+    {
+        // Set the new connection as request attribute and change the status to 101
+        // so that Jetty understands that it has to upgrade the connection
+        request.setAttribute("org.eclipse.jetty.io.Connection", connection);
+        response.setStatus(HttpServletResponse.SC_SWITCHING_PROTOCOLS);
+        LOG.debug("Upgraded connection to {}", connection);
+    }
+
+    private void register(SocketChannel channel, ProxyToServerConnection proxyToServer) throws IOException
+    {
+        _selectorManager.register(channel, proxyToServer);
+        proxyToServer.waitReady(_connectTimeout);
+    }
+
+    /**
+     * <p>Reads (with non-blocking semantic) into the given {@code buffer} from the given {@code endPoint}.</p>
+     *
+     * @param endPoint the endPoint to read from
+     * @param buffer   the buffer to read data into
+     * @param context  the context information related to the connection
+     * @return the number of bytes read (possibly 0 since the read is non-blocking)
+     *         or -1 if the channel has been closed remotely
+     * @throws IOException if the endPoint cannot be read
+     */
+    protected int read(EndPoint endPoint, Buffer buffer, ConcurrentMap<String, Object> context) throws IOException
+    {
+        return endPoint.fill(buffer);
+    }
+
+    /**
+     * <p>Writes (with blocking semantic) the given buffer of data onto the given endPoint.</p>
+     *
+     * @param endPoint the endPoint to write to
+     * @param buffer   the buffer to write
+     * @param context  the context information related to the connection
+     * @throws IOException if the buffer cannot be written
+     * @return the number of bytes written
+     */
+    protected int write(EndPoint endPoint, Buffer buffer, ConcurrentMap<String, Object> context) throws IOException
+    {
+        if (buffer == null)
+            return 0;
+
+        int length = buffer.length();
+        final StringBuilder debug = LOG.isDebugEnabled()?new StringBuilder():null;
+        int flushed = endPoint.flush(buffer);
+        if (debug!=null)
+            debug.append(flushed);
+        
+        // Loop until all written
+        while (buffer.length()>0 && !endPoint.isOutputShutdown())
+        {
+            if (!endPoint.isBlocking())
+            {
+                boolean ready = endPoint.blockWritable(getWriteTimeout());
+                if (!ready)
+                    throw new IOException("Write timeout");
+            }
+            flushed = endPoint.flush(buffer);
+            if (debug!=null)
+                debug.append("+").append(flushed);
+        }
+       
+        LOG.debug("Written {}/{} bytes {}", debug, length, endPoint);
+        buffer.compact();
+        return length;
+    }
+
+    private class Manager extends SelectorManager
+    {
+        @Override
+        protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey key) throws IOException
+        {
+            SelectChannelEndPoint endp = new SelectChannelEndPoint(channel, selectSet, key, channel.socket().getSoTimeout());
+            endp.setConnection(selectSet.getManager().newConnection(channel,endp, key.attachment()));
+            endp.setMaxIdleTime(_writeTimeout);
+            return endp;
+        }
+
+        @Override
+        public AsyncConnection newConnection(SocketChannel channel, AsyncEndPoint endpoint, Object attachment)
+        {
+            ProxyToServerConnection proxyToServer = (ProxyToServerConnection)attachment;
+            proxyToServer.setTimeStamp(System.currentTimeMillis());
+            proxyToServer.setEndPoint(endpoint);
+            return proxyToServer;
+        }
+
+        @Override
+        protected void endPointOpened(SelectChannelEndPoint endpoint)
+        {
+            ProxyToServerConnection proxyToServer = (ProxyToServerConnection)endpoint.getSelectionKey().attachment();
+            proxyToServer.ready();
+        }
+
+        @Override
+        public boolean dispatch(Runnable task)
+        {
+            return _threadPool.dispatch(task);
+        }
+
+        @Override
+        protected void endPointClosed(SelectChannelEndPoint endpoint)
+        {
+        }
+
+        @Override
+        protected void endPointUpgraded(ConnectedEndPoint endpoint, Connection oldConnection)
+        {
+        }
+    }
+
+    public class ProxyToServerConnection implements AsyncConnection
+    {
+        private final CountDownLatch _ready = new CountDownLatch(1);
+        private final Buffer _buffer = new IndirectNIOBuffer(4096);
+        private final ConcurrentMap<String, Object> _context;
+        private volatile Buffer _data;
+        private volatile ClientToProxyConnection _toClient;
+        private volatile long _timestamp;
+        private volatile AsyncEndPoint _endPoint;
+
+        public ProxyToServerConnection(ConcurrentMap<String, Object> context, Buffer data)
+        {
+            _context = context;
+            _data = data;
+        }
+
+        @Override
+        public String toString()
+        {
+            StringBuilder builder = new StringBuilder("ProxyToServer");
+            builder.append("(:").append(_endPoint.getLocalPort());
+            builder.append("<=>:").append(_endPoint.getRemotePort());
+            return builder.append(")").toString();
+        }
+
+        public Connection handle() throws IOException
+        {
+            LOG.debug("{}: begin reading from server", this);
+            try
+            {
+                writeData();
+
+                while (true)
+                {
+                    int read = read(_endPoint, _buffer, _context);
+
+                    if (read == -1)
+                    {
+                        LOG.debug("{}: server closed connection {}", this, _endPoint);
+
+                        if (_endPoint.isOutputShutdown() || !_endPoint.isOpen())
+                            closeClient();
+                        else
+                            _toClient.shutdownOutput();
+
+                        break;
+                    }
+
+                    if (read == 0)
+                        break;
+
+                    LOG.debug("{}: read from server {} bytes {}", this, read, _endPoint);
+                    int written = write(_toClient._endPoint, _buffer, _context);
+                    LOG.debug("{}: written to {} {} bytes", this, _toClient, written);
+                }
+                return this;
+            }
+            catch (ClosedChannelException x)
+            {
+                LOG.debug(x);
+                throw x;
+            }
+            catch (IOException x)
+            {
+                LOG.warn(this + ": unexpected exception", x);
+                close();
+                throw x;
+            }
+            catch (RuntimeException x)
+            {
+                LOG.warn(this + ": unexpected exception", x);
+                close();
+                throw x;
+            }
+            finally
+            {
+                LOG.debug("{}: end reading from server", this);
+            }
+        }
+
+        public void onInputShutdown() throws IOException
+        {
+        }
+
+        private void writeData() throws IOException
+        {
+            // This method is called from handle() and closeServer()
+            // which may happen concurrently (e.g. a client closing
+            // while reading from the server), so needs synchronization
+            synchronized (this)
+            {
+                if (_data != null)
+                {
+                    try
+                    {
+                        int written = write(_endPoint, _data, _context);
+                        LOG.debug("{}: written to server {} bytes", this, written);
+                    }
+                    finally
+                    {
+                        // Attempt once to write the data; if the write fails (for example
+                        // because the connection is already closed), clear the data and
+                        // give up to avoid to continue to write data to a closed connection
+                        _data = null;
+                    }
+                }
+            }
+        }
+
+        public void setConnection(ClientToProxyConnection connection)
+        {
+            _toClient = connection;
+        }
+
+        public long getTimeStamp()
+        {
+            return _timestamp;
+        }
+
+        public void setTimeStamp(long timestamp)
+        {
+            _timestamp = timestamp;
+        }
+
+        public void setEndPoint(AsyncEndPoint endpoint)
+        {
+            _endPoint = endpoint;
+        }
+
+        public boolean isIdle()
+        {
+            return false;
+        }
+
+        public boolean isSuspended()
+        {
+            return false;
+        }
+
+        public void onClose()
+        {
+        }
+
+        public void ready()
+        {
+            _ready.countDown();
+        }
+
+        public void waitReady(long timeout) throws IOException
+        {
+            try
+            {
+                _ready.await(timeout, TimeUnit.MILLISECONDS);
+            }
+            catch (final InterruptedException x)
+            {
+                throw new IOException()
+                {{
+                        initCause(x);
+                    }};
+            }
+        }
+
+        public void closeClient() throws IOException
+        {
+            _toClient.closeClient();
+        }
+
+        public void closeServer() throws IOException
+        {
+            _endPoint.close();
+        }
+
+        public void close()
+        {
+            try
+            {
+                closeClient();
+            }
+            catch (IOException x)
+            {
+                LOG.debug(this + ": unexpected exception closing the client", x);
+            }
+
+            try
+            {
+                closeServer();
+            }
+            catch (IOException x)
+            {
+                LOG.debug(this + ": unexpected exception closing the server", x);
+            }
+        }
+
+        public void shutdownOutput() throws IOException
+        {
+            writeData();
+            _endPoint.shutdownOutput();
+        }
+
+        public void onIdleExpired(long idleForMs)
+        {
+            try
+            {
+                LOG.debug("{} idle expired", this);
+                if (_endPoint.isOutputShutdown())
+                    close();
+                else
+                    shutdownOutput();
+            }
+            catch(Exception e)
+            {
+                LOG.debug(e);
+                close();
+            }
+        }
+    }
+
+    public class ClientToProxyConnection implements AsyncConnection
+    {
+        private final Buffer _buffer = new IndirectNIOBuffer(4096);
+        private final ConcurrentMap<String, Object> _context;
+        private final SocketChannel _channel;
+        private final EndPoint _endPoint;
+        private final long _timestamp;
+        private volatile ProxyToServerConnection _toServer;
+        private boolean _firstTime = true;
+
+        public ClientToProxyConnection(ConcurrentMap<String, Object> context, SocketChannel channel, EndPoint endPoint, long timestamp)
+        {
+            _context = context;
+            _channel = channel;
+            _endPoint = endPoint;
+            _timestamp = timestamp;
+        }
+
+        @Override
+        public String toString()
+        {
+            StringBuilder builder = new StringBuilder("ClientToProxy");
+            builder.append("(:").append(_endPoint.getLocalPort());
+            builder.append("<=>:").append(_endPoint.getRemotePort());
+            return builder.append(")").toString();
+        }
+
+        public Connection handle() throws IOException
+        {
+            LOG.debug("{}: begin reading from client", this);
+            try
+            {
+                if (_firstTime)
+                {
+                    _firstTime = false;
+                    register(_channel, _toServer);
+                    LOG.debug("{}: registered channel {} with connection {}", this, _channel, _toServer);
+                }
+
+                while (true)
+                {
+                    int read = read(_endPoint, _buffer, _context);
+
+                    if (read == -1)
+                    {
+                        LOG.debug("{}: client closed connection {}", this, _endPoint);
+
+                        if (_endPoint.isOutputShutdown() || !_endPoint.isOpen())
+                            closeServer();
+                        else
+                            _toServer.shutdownOutput();
+
+                        break;
+                    }
+
+                    if (read == 0)
+                        break;
+
+                    LOG.debug("{}: read from client {} bytes {}", this, read, _endPoint);
+                    int written = write(_toServer._endPoint, _buffer, _context);
+                    LOG.debug("{}: written to {} {} bytes", this, _toServer, written);
+                }
+                return this;
+            }
+            catch (ClosedChannelException x)
+            {
+                LOG.debug(x);
+                closeServer();
+                throw x;
+            }
+            catch (IOException x)
+            {
+                LOG.warn(this + ": unexpected exception", x);
+                close();
+                throw x;
+            }
+            catch (RuntimeException x)
+            {
+                LOG.warn(this + ": unexpected exception", x);
+                close();
+                throw x;
+            }
+            finally
+            {
+                LOG.debug("{}: end reading from client", this);
+            }
+        }
+
+        public void onInputShutdown() throws IOException
+        {
+        }
+
+        public long getTimeStamp()
+        {
+            return _timestamp;
+        }
+
+        public boolean isIdle()
+        {
+            return false;
+        }
+
+        public boolean isSuspended()
+        {
+            return false;
+        }
+
+        public void onClose()
+        {
+        }
+
+        public void setConnection(ProxyToServerConnection connection)
+        {
+            _toServer = connection;
+        }
+
+        public void closeClient() throws IOException
+        {
+            _endPoint.close();
+        }
+
+        public void closeServer() throws IOException
+        {
+            _toServer.closeServer();
+        }
+
+        public void close()
+        {
+            try
+            {
+                closeClient();
+            }
+            catch (IOException x)
+            {
+                LOG.debug(this + ": unexpected exception closing the client", x);
+            }
+
+            try
+            {
+                closeServer();
+            }
+            catch (IOException x)
+            {
+                LOG.debug(this + ": unexpected exception closing the server", x);
+            }
+        }
+
+        public void shutdownOutput() throws IOException
+        {
+            _endPoint.shutdownOutput();
+        }
+
+        public void onIdleExpired(long idleForMs)
+        {
+            try
+            {
+                LOG.debug("{} idle expired", this);
+                if (_endPoint.isOutputShutdown())
+                    close();
+                else
+                    shutdownOutput();
+            }
+            catch(Exception e)
+            {
+                LOG.debug(e);
+                close();
+            }
+        }
+    }
+
+    /**
+     * Add a whitelist entry to an existing handler configuration
+     *
+     * @param entry new whitelist entry
+     */
+    public void addWhite(String entry)
+    {
+        add(entry, _white);
+    }
+
+    /**
+     * Add a blacklist entry to an existing handler configuration
+     *
+     * @param entry new blacklist entry
+     */
+    public void addBlack(String entry)
+    {
+        add(entry, _black);
+    }
+
+    /**
+     * Re-initialize the whitelist of existing handler object
+     *
+     * @param entries array of whitelist entries
+     */
+    public void setWhite(String[] entries)
+    {
+        set(entries, _white);
+    }
+
+    /**
+     * Re-initialize the blacklist of existing handler object
+     *
+     * @param entries array of blacklist entries
+     */
+    public void setBlack(String[] entries)
+    {
+        set(entries, _black);
+    }
+
+    /**
+     * Helper method to process a list of new entries and replace
+     * the content of the specified host map
+     *
+     * @param entries new entries
+     * @param hostMap target host map
+     */
+    protected void set(String[] entries, HostMap<String> hostMap)
+    {
+        hostMap.clear();
+
+        if (entries != null && entries.length > 0)
+        {
+            for (String addrPath : entries)
+            {
+                add(addrPath, hostMap);
+            }
+        }
+    }
+
+    /**
+     * Helper method to process the new entry and add it to
+     * the specified host map.
+     *
+     * @param entry      new entry
+     * @param hostMap target host map
+     */
+    private void add(String entry, HostMap<String> hostMap)
+    {
+        if (entry != null && entry.length() > 0)
+        {
+            entry = entry.trim();
+            if (hostMap.get(entry) == null)
+            {
+                hostMap.put(entry, entry);
+            }
+        }
+    }
+
+    /**
+     * Check the request hostname against white- and blacklist.
+     *
+     * @param host hostname to check
+     * @return true if hostname is allowed to be proxied
+     */
+    public boolean validateDestination(String host)
+    {
+        if (_white.size() > 0)
+        {
+            Object whiteObj = _white.getLazyMatches(host);
+            if (whiteObj == null)
+            {
+                return false;
+            }
+        }
+
+        if (_black.size() > 0)
+        {
+            Object blackObj = _black.getLazyMatches(host);
+            if (blackObj != null)
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    @Override
+    public void dump(Appendable out, String indent) throws IOException
+    {
+        dumpThis(out);
+        if (_privateThreadPool)
+            dump(out, indent, Arrays.asList(_threadPool, _selectorManager), TypeUtil.asList(getHandlers()), getBeans());
+        else
+            dump(out, indent, Arrays.asList(_selectorManager), TypeUtil.asList(getHandlers()), getBeans());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/handler/ContextHandler.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,2570 @@
+//
+//  ========================================================================
+//  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.server.handler;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.security.AccessController;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.EventListener;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import javax.servlet.DispatcherType;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.Servlet;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextAttributeEvent;
+import javax.servlet.ServletContextAttributeListener;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRegistration;
+import javax.servlet.ServletRequestAttributeListener;
+import javax.servlet.ServletRequestEvent;
+import javax.servlet.ServletRequestListener;
+import javax.servlet.SessionCookieConfig;
+import javax.servlet.SessionTrackingMode;
+import javax.servlet.Filter;
+import javax.servlet.FilterRegistration;
+import javax.servlet.FilterRegistration.Dynamic;
+import javax.servlet.descriptor.JspConfigDescriptor;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.HttpException;
+import org.eclipse.jetty.http.MimeTypes;
+import org.eclipse.jetty.io.Buffer;
+import org.eclipse.jetty.server.AbstractHttpConnection;
+import org.eclipse.jetty.server.Dispatcher;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.HandlerContainer;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.util.Attributes;
+import org.eclipse.jetty.util.AttributesMap;
+import org.eclipse.jetty.util.LazyList;
+import org.eclipse.jetty.util.Loader;
+import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.TypeUtil;
+import org.eclipse.jetty.util.URIUtil;
+import org.eclipse.jetty.util.component.AggregateLifeCycle;
+import org.eclipse.jetty.util.component.Dumpable;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.resource.Resource;
+
+/* ------------------------------------------------------------ */
+/**
+ * ContextHandler.
+ *
+ * This handler wraps a call to handle by setting the context and servlet path, plus setting the context classloader.
+ *
+ * <p>
+ * If the context init parameter "org.eclipse.jetty.server.context.ManagedAttributes" is set to a comma separated list of names, then they are treated as
+ * context attribute names, which if set as attributes are passed to the servers Container so that they may be managed with JMX.
+ * <p>
+ * The maximum size of a form that can be processed by this context is controlled by the system properties org.eclipse.jetty.server.Request.maxFormKeys
+ * and org.eclipse.jetty.server.Request.maxFormContentSize.  These can also be configured with {@link #setMaxFormContentSize(int)} and {@link #setMaxFormKeys(int)}
+ * 
+ * @org.apache.xbean.XBean description="Creates a basic HTTP context"
+ */
+public class ContextHandler extends ScopedHandler implements Attributes, Server.Graceful
+{
+	private static final Logger LOG = Log.getLogger(ContextHandler.class);
+
+	private static final ThreadLocal<Context> __context = new ThreadLocal<Context>();
+
+	/**
+	 * If a context attribute with this name is set, it is interpreted as a comma separated list of attribute name. Any other context attributes that are set
+	 * with a name from this list will result in a call to {@link #setManagedAttribute(String, Object)}, which typically initiates the creation of a JMX MBean
+	 * for the attribute value.
+	 */
+	public static final String MANAGED_ATTRIBUTES = "org.eclipse.jetty.server.context.ManagedAttributes";
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * Get the current ServletContext implementation.
+	 *
+	 * @return ServletContext implementation
+	 */
+	public static Context getCurrentContext()
+	{
+		return __context.get();
+	}
+
+	protected Context _scontext;
+
+	private final AttributesMap _attributes;
+	private final AttributesMap _contextAttributes;
+	private final Map<String, String> _initParams;
+	private ClassLoader _classLoader;
+	private String _contextPath = "/";
+	private String _displayName;
+	private Resource _baseResource;
+	private MimeTypes _mimeTypes;
+	private Map<String, String> _localeEncodingMap;
+	private String[] _welcomeFiles;
+	private ErrorHandler _errorHandler;
+	private String[] _vhosts;
+	private Set<String> _connectors;
+	private EventListener[] _eventListeners;
+	private Logger _logger;
+	private boolean _allowNullPathInfo;
+	private int _maxFormKeys = Integer.getInteger("org.eclipse.jetty.server.Request.maxFormKeys",-1).intValue();
+	private int _maxFormContentSize = Integer.getInteger("org.eclipse.jetty.server.Request.maxFormContentSize",-1).intValue();
+	private boolean _compactPath = false;
+	private boolean _aliasesAllowed = false;
+
+	private Object _contextListeners;
+	private Object _contextAttributeListeners;
+	private Object _requestListeners;
+	private Object _requestAttributeListeners;
+	private Object _durableListeners;
+	private Map<String, Object> _managedAttributes;
+	private String[] _protectedTargets;
+	private final CopyOnWriteArrayList<AliasCheck> _aliasChecks = new CopyOnWriteArrayList<ContextHandler.AliasCheck>();
+
+	private boolean _shutdown = false;
+	private boolean _available = true;
+	private volatile int _availability; // 0=STOPPED, 1=AVAILABLE, 2=SHUTDOWN, 3=UNAVAILABLE
+
+	private final static int __STOPPED = 0, __AVAILABLE = 1, __SHUTDOWN = 2, __UNAVAILABLE = 3;
+
+	/* ------------------------------------------------------------ */
+	/**
+	 *
+	 */
+	public ContextHandler()
+	{
+		super();
+		_scontext = new Context();
+		_attributes = new AttributesMap();
+		_contextAttributes = new AttributesMap();
+		_initParams = new HashMap<String, String>();
+		addAliasCheck(new ApproveNonExistentDirectoryAliases());
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 *
+	 */
+	protected ContextHandler(Context context)
+	{
+		super();
+		_scontext = context;
+		_attributes = new AttributesMap();
+		_contextAttributes = new AttributesMap();
+		_initParams = new HashMap<String, String>();
+		addAliasCheck(new ApproveNonExistentDirectoryAliases());
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 *
+	 */
+	public ContextHandler(String contextPath)
+	{
+		this();
+		setContextPath(contextPath);
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 *
+	 */
+	public ContextHandler(HandlerContainer parent, String contextPath)
+	{
+		this();
+		setContextPath(contextPath);
+		if (parent instanceof HandlerWrapper)
+			((HandlerWrapper)parent).setHandler(this);
+		else if (parent instanceof HandlerCollection)
+			((HandlerCollection)parent).addHandler(this);
+	}
+
+	/* ------------------------------------------------------------ */
+	@Override
+	public void dump(Appendable out, String indent) throws IOException
+	{
+		dumpThis(out);
+		dump(out,indent,Collections.singletonList(new CLDump(getClassLoader())),TypeUtil.asList(getHandlers()),getBeans(),_initParams.entrySet(),
+				_attributes.getAttributeEntrySet(),_contextAttributes.getAttributeEntrySet());
+	}
+
+	/* ------------------------------------------------------------ */
+	public Context getServletContext()
+	{
+		return _scontext;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @return the allowNullPathInfo true if /context is not redirected to /context/
+	 */
+	public boolean getAllowNullPathInfo()
+	{
+		return _allowNullPathInfo;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @param allowNullPathInfo
+	 *            true if /context is not redirected to /context/
+	 */
+	public void setAllowNullPathInfo(boolean allowNullPathInfo)
+	{
+		_allowNullPathInfo = allowNullPathInfo;
+	}
+
+	/* ------------------------------------------------------------ */
+	@Override
+	public void setServer(Server server)
+	{
+		if (_errorHandler != null)
+		{
+			Server old_server = getServer();
+			if (old_server != null && old_server != server)
+				old_server.getContainer().update(this,_errorHandler,null,"error",true);
+			super.setServer(server);
+			if (server != null && server != old_server)
+				server.getContainer().update(this,null,_errorHandler,"error",true);
+			_errorHandler.setServer(server);
+		}
+		else
+			super.setServer(server);
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * Set the virtual hosts for the context. Only requests that have a matching host header or fully qualified URL will be passed to that context with a
+	 * virtual host name. A context with no virtual host names or a null virtual host name is available to all requests that are not served by a context with a
+	 * matching virtual host name.
+	 *
+	 * @param vhosts
+	 *            Array of virtual hosts that this context responds to. A null host name or null/empty array means any hostname is acceptable. Host names may be
+	 *            String representation of IP addresses. Host names may start with '*.' to wildcard one level of names.
+	 */
+	public void setVirtualHosts(String[] vhosts)
+	{
+		if (vhosts == null)
+		{
+			_vhosts = vhosts;
+		}
+		else
+		{
+			_vhosts = new String[vhosts.length];
+			for (int i = 0; i < vhosts.length; i++)
+				_vhosts[i] = normalizeHostname(vhosts[i]);
+		}
+	}
+
+	/* ------------------------------------------------------------ */
+	/** Either set virtual hosts or add to an existing set of virtual hosts.
+	 *
+	 * @param virtualHosts
+	 *            Array of virtual hosts that this context responds to. A null host name or null/empty array means any hostname is acceptable. Host names may be
+	 *            String representation of IP addresses. Host names may start with '*.' to wildcard one level of names.
+	 */
+	public void addVirtualHosts(String[] virtualHosts)
+	{
+		if (virtualHosts == null)  // since this is add, we don't null the old ones
+		{
+			return;
+		}
+		else
+		{
+			List<String> currentVirtualHosts = null;
+			if (_vhosts != null)
+			{
+				currentVirtualHosts = new ArrayList<String>(Arrays.asList(_vhosts));
+			}
+			else
+			{
+				currentVirtualHosts = new ArrayList<String>();
+			}
+
+			for (int i = 0; i < virtualHosts.length; i++)
+			{
+				String normVhost = normalizeHostname(virtualHosts[i]);
+				if (!currentVirtualHosts.contains(normVhost))
+				{
+					currentVirtualHosts.add(normVhost);
+				}
+			}
+			_vhosts = currentVirtualHosts.toArray(new String[0]);
+		}
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * Removes an array of virtual host entries, if this removes all entries the _vhosts will be set to null
+	 *
+	 *  @param virtualHosts
+	 *            Array of virtual hosts that this context responds to. A null host name or null/empty array means any hostname is acceptable. Host names may be
+	 *            String representation of IP addresses. Host names may start with '*.' to wildcard one level of names.
+	 */
+	public void removeVirtualHosts(String[] virtualHosts)
+	{
+		if (virtualHosts == null)
+		{
+			return; // do nothing
+		}
+		else if ( _vhosts == null || _vhosts.length == 0)
+		{
+			return; // do nothing
+		}
+		else
+		{
+			List<String> existingVirtualHosts = new ArrayList<String>(Arrays.asList(_vhosts));
+
+			for (int i = 0; i < virtualHosts.length; i++)
+			{
+				String toRemoveVirtualHost = normalizeHostname(virtualHosts[i]);
+				if (existingVirtualHosts.contains(toRemoveVirtualHost))
+				{
+					existingVirtualHosts.remove(toRemoveVirtualHost);
+				}
+			}
+
+			if (existingVirtualHosts.isEmpty())
+			{
+				_vhosts = null; // if we ended up removing them all, just null out _vhosts
+			}
+			else
+			{
+				_vhosts = existingVirtualHosts.toArray(new String[0]);
+			}
+		}
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * Get the virtual hosts for the context. Only requests that have a matching host header or fully qualified URL will be passed to that context with a
+	 * virtual host name. A context with no virtual host names or a null virtual host name is available to all requests that are not served by a context with a
+	 * matching virtual host name.
+	 *
+	 * @return Array of virtual hosts that this context responds to. A null host name or empty array means any hostname is acceptable. Host names may be String
+	 *         representation of IP addresses. Host names may start with '*.' to wildcard one level of names.
+	 */
+	public String[] getVirtualHosts()
+	{
+		return _vhosts;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @return an array of connector names that this context will accept a request from.
+	 */
+	public String[] getConnectorNames()
+	{
+		if (_connectors == null || _connectors.size() == 0)
+			return null;
+
+		return _connectors.toArray(new String[_connectors.size()]);
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * Set the names of accepted connectors.
+	 *
+	 * Names are either "host:port" or a specific configured name for a connector.
+	 *
+	 * @param connectors
+	 *            If non null, an array of connector names that this context will accept a request from.
+	 */
+	public void setConnectorNames(String[] connectors)
+	{
+		if (connectors == null || connectors.length == 0)
+			_connectors = null;
+		else
+			_connectors = new HashSet<String>(Arrays.asList(connectors));
+	}
+
+	/* ------------------------------------------------------------ */
+	/*
+	 * @see javax.servlet.ServletContext#getAttribute(java.lang.String)
+	 */
+	public Object getAttribute(String name)
+	{
+		return _attributes.getAttribute(name);
+	}
+
+	/* ------------------------------------------------------------ */
+	/*
+	 * @see javax.servlet.ServletContext#getAttributeNames()
+	 */
+	@SuppressWarnings("unchecked")
+	public Enumeration getAttributeNames()
+	{
+		return AttributesMap.getAttributeNamesCopy(_attributes);
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @return Returns the attributes.
+	 */
+	public Attributes getAttributes()
+	{
+		return _attributes;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @return Returns the classLoader.
+	 */
+	public ClassLoader getClassLoader()
+	{
+		return _classLoader;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * Make best effort to extract a file classpath from the context classloader
+	 *
+	 * @return Returns the classLoader.
+	 */
+	public String getClassPath()
+	{
+		if (_classLoader == null || !(_classLoader instanceof URLClassLoader))
+			return null;
+		URLClassLoader loader = (URLClassLoader)_classLoader;
+		URL[] urls = loader.getURLs();
+		StringBuilder classpath = new StringBuilder();
+		for (int i = 0; i < urls.length; i++)
+		{
+			try
+			{
+				Resource resource = newResource(urls[i]);
+				File file = resource.getFile();
+				if (file != null && file.exists())
+				{
+					if (classpath.length() > 0)
+						classpath.append(File.pathSeparatorChar);
+					classpath.append(file.getAbsolutePath());
+				}
+			}
+			catch (IOException e)
+			{
+				LOG.debug(e);
+			}
+		}
+		if (classpath.length() == 0)
+			return null;
+		return classpath.toString();
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @return Returns the _contextPath.
+	 */
+	public String getContextPath()
+	{
+		return _contextPath;
+	}
+
+	/* ------------------------------------------------------------ */
+	/*
+	 * @see javax.servlet.ServletContext#getInitParameter(java.lang.String)
+	 */
+	public String getInitParameter(String name)
+	{
+		return _initParams.get(name);
+	}
+
+	/* ------------------------------------------------------------ */
+	/*
+	 */
+	public String setInitParameter(String name, String value)
+	{
+		return _initParams.put(name,value);
+	}
+
+	/* ------------------------------------------------------------ */
+	/*
+	 * @see javax.servlet.ServletContext#getInitParameterNames()
+	 */
+	@SuppressWarnings("rawtypes")
+	public Enumeration getInitParameterNames()
+	{
+		return Collections.enumeration(_initParams.keySet());
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @return Returns the initParams.
+	 */
+	public Map<String, String> getInitParams()
+	{
+		return _initParams;
+	}
+
+	/* ------------------------------------------------------------ */
+	/*
+	 * @see javax.servlet.ServletContext#getServletContextName()
+	 */
+	public String getDisplayName()
+	{
+		return _displayName;
+	}
+
+	/* ------------------------------------------------------------ */
+	public EventListener[] getEventListeners()
+	{
+		return _eventListeners;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * Set the context event listeners.
+	 *
+	 * @param eventListeners
+	 *            the event listeners
+	 * @see ServletContextListener
+	 * @see ServletContextAttributeListener
+	 * @see ServletRequestListener
+	 * @see ServletRequestAttributeListener
+	 */
+	public void setEventListeners(EventListener[] eventListeners)
+	{
+		_contextListeners = null;
+		_contextAttributeListeners = null;
+		_requestListeners = null;
+		_requestAttributeListeners = null;
+
+		_eventListeners = eventListeners;
+
+		for (int i = 0; eventListeners != null && i < eventListeners.length; i++)
+		{
+			EventListener listener = _eventListeners[i];
+
+			if (listener instanceof ServletContextListener)
+				_contextListeners = LazyList.add(_contextListeners,listener);
+
+			if (listener instanceof ServletContextAttributeListener)
+				_contextAttributeListeners = LazyList.add(_contextAttributeListeners,listener);
+
+			if (listener instanceof ServletRequestListener)
+				_requestListeners = LazyList.add(_requestListeners,listener);
+
+			if (listener instanceof ServletRequestAttributeListener)
+				_requestAttributeListeners = LazyList.add(_requestAttributeListeners,listener);
+		}
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * Add a context event listeners.
+	 *
+	 * @see ServletContextListener
+	 * @see ServletContextAttributeListener
+	 * @see ServletRequestListener
+	 * @see ServletRequestAttributeListener
+	 */
+	public void addEventListener(EventListener listener)
+	{
+		//Only listeners added before the context starts last through a stop/start cycle
+		if (!(isStarted() || isStarting()))
+			_durableListeners = LazyList.add(_durableListeners, listener);
+		
+		setEventListeners((EventListener[])LazyList.addToArray(getEventListeners(),listener,EventListener.class));
+	}
+	
+   
+	/**
+	 * Apply any necessary restrictions on a programmatically added
+	 * listener.
+	 * 
+	 * Superclasses should implement.
+	 * 
+	 * @param listener
+	 */
+	public void restrictEventListener (EventListener listener)
+	{
+	}
+
+	
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @return true if this context is accepting new requests
+	 */
+	public boolean isShutdown()
+	{
+		synchronized (this)
+		{
+			return !_shutdown;
+		}
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * Set shutdown status. This field allows for graceful shutdown of a context. A started context may be put into non accepting state so that existing
+	 * requests can complete, but no new requests are accepted.
+	 *
+	 * @param shutdown
+	 *            true if this context is (not?) accepting new requests
+	 */
+	public void setShutdown(boolean shutdown)
+	{
+		synchronized (this)
+		{
+			_shutdown = shutdown;
+			_availability = isRunning()?(_shutdown?__SHUTDOWN:_available?__AVAILABLE:__UNAVAILABLE):__STOPPED;
+		}
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @return false if this context is unavailable (sends 503)
+	 */
+	public boolean isAvailable()
+	{
+		synchronized (this)
+		{
+			return _available;
+		}
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * Set Available status.
+	 */
+	public void setAvailable(boolean available)
+	{
+		synchronized (this)
+		{
+			_available = available;
+			_availability = isRunning()?(_shutdown?__SHUTDOWN:_available?__AVAILABLE:__UNAVAILABLE):__STOPPED;
+		}
+	}
+
+	/* ------------------------------------------------------------ */
+	public Logger getLogger()
+	{
+		return _logger;
+	}
+
+	/* ------------------------------------------------------------ */
+	public void setLogger(Logger logger)
+	{
+		_logger = logger;
+	}
+
+	/* ------------------------------------------------------------ */
+	/*
+	 * @see org.eclipse.thread.AbstractLifeCycle#doStart()
+	 */
+	@Override
+	protected void doStart() throws Exception
+	{
+		_availability = __STOPPED;
+
+		if (_contextPath == null)
+			throw new IllegalStateException("Null contextPath");
+
+		_logger = Log.getLogger(getDisplayName() == null?getContextPath():getDisplayName());
+		ClassLoader old_classloader = null;
+		Thread current_thread = null;
+		Context old_context = null;
+
+		try
+		{
+			// Set the classloader
+			if (_classLoader != null)
+			{
+				current_thread = Thread.currentThread();
+				old_classloader = current_thread.getContextClassLoader();
+				current_thread.setContextClassLoader(_classLoader);
+			}
+
+			if (_mimeTypes == null)
+				_mimeTypes = new MimeTypes();
+
+			old_context = __context.get();
+			__context.set(_scontext);
+
+			// defers the calling of super.doStart()
+			startContext();
+
+			synchronized(this)
+			{
+				_availability = _shutdown?__SHUTDOWN:_available?__AVAILABLE:__UNAVAILABLE;
+			}
+		}
+		finally
+		{
+			__context.set(old_context);
+
+			// reset the classloader
+			if (_classLoader != null)
+			{
+				current_thread.setContextClassLoader(old_classloader);
+			}
+
+		}
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * Extensible startContext. this method is called from {@link ContextHandler#doStart()} instead of a call to super.doStart(). This allows derived classes to
+	 * insert additional handling (Eg configuration) before the call to super.doStart by this method will start contained handlers.
+	 *
+	 * @see org.eclipse.jetty.server.handler.ContextHandler.Context
+	 */
+	protected void startContext() throws Exception
+	{
+		String managedAttributes = _initParams.get(MANAGED_ATTRIBUTES);
+		if (managedAttributes != null)
+		{
+			_managedAttributes = new HashMap<String, Object>();
+			String[] attributes = managedAttributes.split(",");
+			for (String attribute : attributes)
+				_managedAttributes.put(attribute,null);
+
+			Enumeration e = _scontext.getAttributeNames();
+			while (e.hasMoreElements())
+			{
+				String name = (String)e.nextElement();
+				Object value = _scontext.getAttribute(name);
+				checkManagedAttribute(name,value);
+			}
+		}
+
+		super.doStart();
+
+		if (_errorHandler != null)
+			_errorHandler.start();
+
+		// Context listeners
+		if (_contextListeners != null)
+		{
+			ServletContextEvent event = new ServletContextEvent(_scontext);
+			for (int i = 0; i < LazyList.size(_contextListeners); i++)
+			{
+				callContextInitialized(((ServletContextListener)LazyList.get(_contextListeners, i)), event);
+			}
+		}
+	}
+
+	/* ------------------------------------------------------------ */
+	public void callContextInitialized (ServletContextListener l, ServletContextEvent e)
+	{
+		l.contextInitialized(e);
+	}
+
+	/* ------------------------------------------------------------ */
+	public void callContextDestroyed (ServletContextListener l, ServletContextEvent e)
+	{
+		l.contextDestroyed(e);
+	}
+	
+	/* ------------------------------------------------------------ */
+	/*
+	 * @see org.eclipse.thread.AbstractLifeCycle#doStop()
+	 */
+	@Override
+	protected void doStop() throws Exception
+	{
+		_availability = __STOPPED;
+
+		ClassLoader old_classloader = null;
+		Thread current_thread = null;
+
+		Context old_context = __context.get();
+		__context.set(_scontext);
+		try
+		{
+			// Set the classloader
+			if (_classLoader != null)
+			{
+				current_thread = Thread.currentThread();
+				old_classloader = current_thread.getContextClassLoader();
+				current_thread.setContextClassLoader(_classLoader);
+			}
+
+			super.doStop();
+
+			// Context listeners
+			if (_contextListeners != null)
+			{
+				ServletContextEvent event = new ServletContextEvent(_scontext);
+				for (int i = LazyList.size(_contextListeners); i-- > 0;)
+				{
+					((ServletContextListener)LazyList.get(_contextListeners,i)).contextDestroyed(event);
+				}
+			}
+			
+			//remove all non-durable listeners
+			setEventListeners((EventListener[])LazyList.toArray(_durableListeners, EventListener.class));
+			_durableListeners = null;
+
+			if (_errorHandler != null)
+				_errorHandler.stop();
+
+			Enumeration e = _scontext.getAttributeNames();
+			while (e.hasMoreElements())
+			{
+				String name = (String)e.nextElement();
+				checkManagedAttribute(name,null);
+			}
+		}
+		finally
+		{
+			LOG.info("stopped {}",this);
+			__context.set(old_context);
+			// reset the classloader
+			if (_classLoader != null)
+				current_thread.setContextClassLoader(old_classloader);
+		}
+
+		_contextAttributes.clearAttributes();
+	}
+
+	/* ------------------------------------------------------------ */
+	/*
+	 * @see org.eclipse.jetty.server.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+	 */
+	public boolean checkContext(final String target, final Request baseRequest, final HttpServletResponse response) throws IOException, ServletException
+	{
+		DispatcherType dispatch = baseRequest.getDispatcherType();
+
+		switch (_availability)
+		{
+			case __STOPPED:
+			case __SHUTDOWN:
+				return false;
+			case __UNAVAILABLE:
+				baseRequest.setHandled(true);
+				response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
+				return false;
+			default:
+				if ((DispatcherType.REQUEST.equals(dispatch) && baseRequest.isHandled()))
+					return false;
+		}
+
+		// Check the vhosts
+		if (_vhosts != null && _vhosts.length > 0)
+		{
+			String vhost = normalizeHostname(baseRequest.getServerName());
+
+			boolean match = false;
+
+			// TODO non-linear lookup
+			for (int i = 0; !match && i < _vhosts.length; i++)
+			{
+				String contextVhost = _vhosts[i];
+				if (contextVhost == null)
+					continue;
+				if (contextVhost.startsWith("*."))
+				{
+					// wildcard only at the beginning, and only for one additional subdomain level
+					match = contextVhost.regionMatches(true,2,vhost,vhost.indexOf(".") + 1,contextVhost.length() - 2);
+				}
+				else
+					match = contextVhost.equalsIgnoreCase(vhost);
+			}
+			if (!match)
+				return false;
+		}
+
+		// Check the connector
+		if (_connectors != null && _connectors.size() > 0)
+		{
+			String connector = AbstractHttpConnection.getCurrentConnection().getConnector().getName();
+			if (connector == null || !_connectors.contains(connector))
+				return false;
+		}
+
+		// Are we not the root context?
+		if (_contextPath.length() > 1)
+		{
+			// reject requests that are not for us
+			if (!target.startsWith(_contextPath))
+				return false;
+			if (target.length() > _contextPath.length() && target.charAt(_contextPath.length()) != '/')
+				return false;
+
+			// redirect null path infos
+			if (!_allowNullPathInfo && _contextPath.length() == target.length())
+			{
+				// context request must end with /
+				baseRequest.setHandled(true);
+				if (baseRequest.getQueryString() != null)
+					response.sendRedirect(URIUtil.addPaths(baseRequest.getRequestURI(),URIUtil.SLASH) + "?" + baseRequest.getQueryString());
+				else
+					response.sendRedirect(URIUtil.addPaths(baseRequest.getRequestURI(),URIUtil.SLASH));
+				return false;
+			}
+		}
+
+		return true;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @see org.eclipse.jetty.server.handler.ScopedHandler#doScope(java.lang.String, org.eclipse.jetty.server.Request, javax.servlet.http.HttpServletRequest,
+	 *      javax.servlet.http.HttpServletResponse)
+	 */
+	@Override
+	public void doScope(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+	{
+		if (LOG.isDebugEnabled())
+			LOG.debug("scope {}|{}|{} @ {}",baseRequest.getContextPath(),baseRequest.getServletPath(),baseRequest.getPathInfo(),this);
+
+		Context old_context = null;
+		String old_context_path = null;
+		String old_servlet_path = null;
+		String old_path_info = null;
+		ClassLoader old_classloader = null;
+		Thread current_thread = null;
+		String pathInfo = target;
+
+		DispatcherType dispatch = baseRequest.getDispatcherType();
+
+		old_context = baseRequest.getContext();
+
+		// Are we already in this context?
+		if (old_context != _scontext)
+		{
+			// check the target.
+			if (DispatcherType.REQUEST.equals(dispatch) || DispatcherType.ASYNC.equals(dispatch) || (DispatcherType.ERROR.equals(dispatch) && baseRequest.getAsyncContinuation().isExpired()))
+			{
+				if (_compactPath)
+					target = URIUtil.compactPath(target);
+				if (!checkContext(target,baseRequest,response))
+					return;
+
+				if (target.length() > _contextPath.length())
+				{
+					if (_contextPath.length() > 1)
+						target = target.substring(_contextPath.length());
+					pathInfo = target;
+				}
+				else if (_contextPath.length() == 1)
+				{
+					target = URIUtil.SLASH;
+					pathInfo = URIUtil.SLASH;
+				}
+				else
+				{
+					target = URIUtil.SLASH;
+					pathInfo = null;
+				}
+			}
+
+			// Set the classloader
+			if (_classLoader != null)
+			{
+				current_thread = Thread.currentThread();
+				old_classloader = current_thread.getContextClassLoader();
+				current_thread.setContextClassLoader(_classLoader);
+			}
+		}
+
+		try
+		{
+			old_context_path = baseRequest.getContextPath();
+			old_servlet_path = baseRequest.getServletPath();
+			old_path_info = baseRequest.getPathInfo();
+
+			// Update the paths
+			baseRequest.setContext(_scontext);
+			__context.set(_scontext);
+			if (!DispatcherType.INCLUDE.equals(dispatch) && target.startsWith("/"))
+			{
+				if (_contextPath.length() == 1)
+					baseRequest.setContextPath("");
+				else
+					baseRequest.setContextPath(_contextPath);
+				baseRequest.setServletPath(null);
+				baseRequest.setPathInfo(pathInfo);
+			}
+
+			if (LOG.isDebugEnabled())
+				LOG.debug("context={}|{}|{} @ {}",baseRequest.getContextPath(),baseRequest.getServletPath(), baseRequest.getPathInfo(),this);
+
+			// start manual inline of nextScope(target,baseRequest,request,response);
+			if (never())
+				nextScope(target,baseRequest,request,response);
+			else if (_nextScope != null)
+				_nextScope.doScope(target,baseRequest,request,response);
+			else if (_outerScope != null)
+				_outerScope.doHandle(target,baseRequest,request,response);
+			else
+				doHandle(target,baseRequest,request,response);
+			// end manual inline (pathentic attempt to reduce stack depth)
+		}
+		finally
+		{
+			if (old_context != _scontext)
+			{
+				// reset the classloader
+				if (_classLoader != null)
+				{
+					current_thread.setContextClassLoader(old_classloader);
+				}
+
+				// reset the context and servlet path.
+				baseRequest.setContext(old_context);
+				__context.set(old_context);
+				baseRequest.setContextPath(old_context_path);
+				baseRequest.setServletPath(old_servlet_path);
+				baseRequest.setPathInfo(old_path_info);
+			}
+		}
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @see org.eclipse.jetty.server.handler.ScopedHandler#doHandle(java.lang.String, org.eclipse.jetty.server.Request, javax.servlet.http.HttpServletRequest,
+	 *      javax.servlet.http.HttpServletResponse)
+	 */
+	@Override
+	public void doHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+	{
+		final DispatcherType dispatch = baseRequest.getDispatcherType();
+		final boolean new_context = baseRequest.takeNewContext();
+		try
+		{
+			if (new_context)
+			{
+				// Handle the REALLY SILLY request events!
+				if (_requestAttributeListeners != null)
+				{
+					final int s = LazyList.size(_requestAttributeListeners);
+					for (int i = 0; i < s; i++)
+						baseRequest.addEventListener(((EventListener)LazyList.get(_requestAttributeListeners,i)));
+				}
+
+				if (_requestListeners != null)
+				{
+					final int s = LazyList.size(_requestListeners);
+					final ServletRequestEvent sre = new ServletRequestEvent(_scontext,request);
+					for (int i = 0; i < s; i++)
+						((ServletRequestListener)LazyList.get(_requestListeners,i)).requestInitialized(sre);
+				}
+			}
+
+			if (DispatcherType.REQUEST.equals(dispatch) && isProtectedTarget(target))
+				throw new HttpException(HttpServletResponse.SC_NOT_FOUND);
+
+			// start manual inline of nextHandle(target,baseRequest,request,response);
+			// noinspection ConstantIfStatement
+			if (never())
+				nextHandle(target,baseRequest,request,response);
+			else if (_nextScope != null && _nextScope == _handler)
+				_nextScope.doHandle(target,baseRequest,request,response);
+			else if (_handler != null)
+				_handler.handle(target,baseRequest,request,response);
+			// end manual inline
+		}
+		catch (HttpException e)
+		{
+			LOG.debug(e);
+			baseRequest.setHandled(true);
+			response.sendError(e.getStatus(),e.getReason());
+		}
+		finally
+		{
+			// Handle more REALLY SILLY request events!
+			if (new_context)
+			{
+				if (_requestListeners != null)
+				{
+					final ServletRequestEvent sre = new ServletRequestEvent(_scontext,request);
+					for (int i = LazyList.size(_requestListeners); i-- > 0;)
+						((ServletRequestListener)LazyList.get(_requestListeners,i)).requestDestroyed(sre);
+				}
+
+				if (_requestAttributeListeners != null)
+				{
+					for (int i = LazyList.size(_requestAttributeListeners); i-- > 0;)
+						baseRequest.removeEventListener(((EventListener)LazyList.get(_requestAttributeListeners,i)));
+				}
+			}
+		}
+	}
+
+	/* ------------------------------------------------------------ */
+	/*
+	 * Handle a runnable in this context
+	 */
+	public void handle(Runnable runnable)
+	{
+		ClassLoader old_classloader = null;
+		Thread current_thread = null;
+		Context old_context = null;
+		try
+		{
+			old_context = __context.get();
+			__context.set(_scontext);
+
+			// Set the classloader
+			if (_classLoader != null)
+			{
+				current_thread = Thread.currentThread();
+				old_classloader = current_thread.getContextClassLoader();
+				current_thread.setContextClassLoader(_classLoader);
+			}
+
+			runnable.run();
+		}
+		finally
+		{
+			__context.set(old_context);
+			if (old_classloader != null)
+			{
+				current_thread.setContextClassLoader(old_classloader);
+			}
+		}
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * Check the target. Called by {@link #handle(String, Request, HttpServletRequest, HttpServletResponse)} when a target within a context is determined. If
+	 * the target is protected, 404 is returned. 
+	 */
+	/* ------------------------------------------------------------ */
+	public boolean isProtectedTarget(String target)
+	{
+		if (target == null || _protectedTargets == null)
+			return false;
+		
+		while (target.startsWith("//"))
+			target=URIUtil.compactPath(target);
+		
+		boolean isProtected = false;
+		int i=0;
+		while (!isProtected && i<_protectedTargets.length)
+		{
+			isProtected = StringUtil.startsWithIgnoreCase(target, _protectedTargets[i++]);
+		}
+		return isProtected;
+	}
+	
+	
+	public void setProtectedTargets (String[] targets)
+	{
+		if (targets == null)
+		{
+			_protectedTargets = null;
+			return;
+		}
+		
+		_protectedTargets = new String[targets.length];
+		System.arraycopy(targets, 0, _protectedTargets, 0, targets.length);
+	}
+	
+	public String[] getProtectedTargets ()
+	{
+		if (_protectedTargets == null)
+			return null;
+		
+		String[] tmp = new String[_protectedTargets.length];
+		System.arraycopy(_protectedTargets, 0, tmp, 0, _protectedTargets.length);
+		return tmp;
+	}
+	
+
+	/* ------------------------------------------------------------ */
+	/*
+	 * @see javax.servlet.ServletContext#removeAttribute(java.lang.String)
+	 */
+	public void removeAttribute(String name)
+	{
+		checkManagedAttribute(name,null);
+		_attributes.removeAttribute(name);
+	}
+
+	/* ------------------------------------------------------------ */
+	/*
+	 * Set a context attribute. Attributes set via this API cannot be overriden by the ServletContext.setAttribute API. Their lifecycle spans the stop/start of
+	 * a context. No attribute listener events are triggered by this API.
+	 *
+	 * @see javax.servlet.ServletContext#setAttribute(java.lang.String, java.lang.Object)
+	 */
+	public void setAttribute(String name, Object value)
+	{
+		checkManagedAttribute(name,value);
+		_attributes.setAttribute(name,value);
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @param attributes
+	 *            The attributes to set.
+	 */
+	public void setAttributes(Attributes attributes)
+	{
+		_attributes.clearAttributes();
+		_attributes.addAll(attributes);
+		Enumeration e = _attributes.getAttributeNames();
+		while (e.hasMoreElements())
+		{
+			String name = (String)e.nextElement();
+			checkManagedAttribute(name,attributes.getAttribute(name));
+		}
+	}
+
+	/* ------------------------------------------------------------ */
+	public void clearAttributes()
+	{
+		Enumeration e = _attributes.getAttributeNames();
+		while (e.hasMoreElements())
+		{
+			String name = (String)e.nextElement();
+			checkManagedAttribute(name,null);
+		}
+		_attributes.clearAttributes();
+	}
+
+	/* ------------------------------------------------------------ */
+	public void checkManagedAttribute(String name, Object value)
+	{
+		if (_managedAttributes != null && _managedAttributes.containsKey(name))
+		{
+			setManagedAttribute(name,value);
+		}
+	}
+
+	/* ------------------------------------------------------------ */
+	public void setManagedAttribute(String name, Object value)
+	{
+		Object old = _managedAttributes.put(name,value);
+		getServer().getContainer().update(this,old,value,name,true);
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @param classLoader
+	 *            The classLoader to set.
+	 */
+	public void setClassLoader(ClassLoader classLoader)
+	{
+		_classLoader = classLoader;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @param contextPath
+	 *            The _contextPath to set.
+	 */
+	public void setContextPath(String contextPath)
+	{
+		if (contextPath != null && contextPath.length() > 1 && contextPath.endsWith("/"))
+			throw new IllegalArgumentException("ends with /");
+		_contextPath = contextPath;
+
+		if (getServer() != null && (getServer().isStarting() || getServer().isStarted()))
+		{
+			Handler[] contextCollections = getServer().getChildHandlersByClass(ContextHandlerCollection.class);
+			for (int h = 0; contextCollections != null && h < contextCollections.length; h++)
+				((ContextHandlerCollection)contextCollections[h]).mapContexts();
+		}
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @param servletContextName
+	 *            The servletContextName to set.
+	 */
+	public void setDisplayName(String servletContextName)
+	{
+		_displayName = servletContextName;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @return Returns the resourceBase.
+	 */
+	public Resource getBaseResource()
+	{
+		if (_baseResource == null)
+			return null;
+		return _baseResource;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @return Returns the base resource as a string.
+	 */
+	public String getResourceBase()
+	{
+		if (_baseResource == null)
+			return null;
+		return _baseResource.toString();
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @param base
+	 *            The resourceBase to set.
+	 */
+	public void setBaseResource(Resource base)
+	{
+		_baseResource = base;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @param resourceBase
+	 *            The base resource as a string.
+	 */
+	public void setResourceBase(String resourceBase)
+	{
+		try
+		{
+			setBaseResource(newResource(resourceBase));
+		}
+		catch (Exception e)
+		{
+			LOG.warn(e.toString());
+			LOG.debug(e);
+			throw new IllegalArgumentException(resourceBase);
+		}
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @return True if aliases are allowed
+	 */
+	public boolean isAliases()
+	{
+		return _aliasesAllowed;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @param aliases
+	 *            aliases are allowed
+	 */
+	public void setAliases(boolean aliases)
+	{
+		_aliasesAllowed = aliases;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @return Returns the mimeTypes.
+	 */
+	public MimeTypes getMimeTypes()
+	{
+		if (_mimeTypes == null)
+			_mimeTypes = new MimeTypes();
+		return _mimeTypes;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @param mimeTypes
+	 *            The mimeTypes to set.
+	 */
+	public void setMimeTypes(MimeTypes mimeTypes)
+	{
+		_mimeTypes = mimeTypes;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 */
+	public void setWelcomeFiles(String[] files)
+	{
+		_welcomeFiles = files;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @return The names of the files which the server should consider to be welcome files in this context.
+	 * @see <a href="http://jcp.org/aboutJava/communityprocess/final/jsr154/index.html">The Servlet Specification</a>
+	 * @see #setWelcomeFiles
+	 */
+	public String[] getWelcomeFiles()
+	{
+		return _welcomeFiles;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @return Returns the errorHandler.
+	 */
+	public ErrorHandler getErrorHandler()
+	{
+		return _errorHandler;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @param errorHandler
+	 *            The errorHandler to set.
+	 */
+	public void setErrorHandler(ErrorHandler errorHandler)
+	{
+		if (errorHandler != null)
+			errorHandler.setServer(getServer());
+		if (getServer() != null)
+			getServer().getContainer().update(this,_errorHandler,errorHandler,"errorHandler",true);
+		_errorHandler = errorHandler;
+	}
+
+	/* ------------------------------------------------------------ */
+	public int getMaxFormContentSize()
+	{
+		return _maxFormContentSize;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * Set the maximum size of a form post, to protect against DOS attacks from large forms.
+	 * @param maxSize
+	 */
+	public void setMaxFormContentSize(int maxSize)
+	{
+		_maxFormContentSize = maxSize;
+	}
+
+	/* ------------------------------------------------------------ */
+	public int getMaxFormKeys()
+	{
+		return _maxFormKeys;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * Set the maximum number of form Keys to protect against DOS attack from crafted hash keys.
+	 * @param max
+	 */
+	public void setMaxFormKeys(int max)
+	{
+		_maxFormKeys = max;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @return True if URLs are compacted to replace multiple '/'s with a single '/'
+	 */
+	public boolean isCompactPath()
+	{
+		return _compactPath;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * @param compactPath
+	 *            True if URLs are compacted to replace multiple '/'s with a single '/'
+	 */
+	public void setCompactPath(boolean compactPath)
+	{
+		_compactPath = compactPath;
+	}
+
+	/* ------------------------------------------------------------ */
+	@Override
+	public String toString()
+	{
+		String[] vhosts = getVirtualHosts();
+
+		StringBuilder b = new StringBuilder();
+
+		Package pkg = getClass().getPackage();
+		if (pkg != null)
+		{
+			String p = pkg.getName();
+			if (p != null && p.length() > 0)
+			{
+				String[] ss = p.split("\\.");
+				for (String s : ss)
+					b.append(s.charAt(0)).append('.');
+			}
+		}
+		b.append(getClass().getSimpleName());
+		b.append('{').append(getContextPath()).append(',').append(getBaseResource());
+
+		if (vhosts != null && vhosts.length > 0)
+			b.append(',').append(vhosts[0]);
+		b.append('}');
+
+		return b.toString();
+	}
+
+	/* ------------------------------------------------------------ */
+	public synchronized Class<?> loadClass(String className) throws ClassNotFoundException
+	{
+		if (className == null)
+			return null;
+
+		if (_classLoader == null)
+			return Loader.loadClass(this.getClass(),className);
+
+		return _classLoader.loadClass(className);
+	}
+
+	/* ------------------------------------------------------------ */
+	public void addLocaleEncoding(String locale, String encoding)
+	{
+		if (_localeEncodingMap == null)
+			_localeEncodingMap = new HashMap<String, String>();
+		_localeEncodingMap.put(locale,encoding);
+	}
+
+	/* ------------------------------------------------------------ */
+	public String getLocaleEncoding(String locale)
+	{
+		if (_localeEncodingMap == null)
+			return null;
+		String encoding = _localeEncodingMap.get(locale);
+		return encoding;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * Get the character encoding for a locale. The full locale name is first looked up in the map of encodings. If no encoding is found, then the locale
+	 * language is looked up.
+	 *
+	 * @param locale
+	 *            a <code>Locale</code> value
+	 * @return a <code>String</code> representing the character encoding for the locale or null if none found.
+	 */
+	public String getLocaleEncoding(Locale locale)
+	{
+		if (_localeEncodingMap == null)
+			return null;
+		String encoding = _localeEncodingMap.get(locale.toString());
+		if (encoding == null)
+			encoding = _localeEncodingMap.get(locale.getLanguage());
+		return encoding;
+	}
+
+	/* ------------------------------------------------------------ */
+	/*
+	 */
+	public Resource getResource(String path) throws MalformedURLException
+	{
+		if (path == null || !path.startsWith(URIUtil.SLASH))
+			throw new MalformedURLException(path);
+
+		if (_baseResource == null)
+			return null;
+
+		try
+		{
+			path = URIUtil.canonicalPath(path);
+			Resource resource = _baseResource.addPath(path);
+			
+			if (checkAlias(path,resource))
+				return resource;
+			return null;
+		}
+		catch (Exception e)
+		{
+			LOG.ignore(e);
+		}
+
+		return null;
+	}
+
+	/* ------------------------------------------------------------ */
+	public boolean checkAlias(String path, Resource resource)
+	{
+		// Is the resource aliased?
+		if (!_aliasesAllowed && resource.getAlias() != null)
+		{
+			if (LOG.isDebugEnabled())
+				LOG.debug("Aliased resource: " + resource + "~=" + resource.getAlias());
+
+			// alias checks
+			for (Iterator<AliasCheck> i=_aliasChecks.iterator();i.hasNext();)
+			{
+				AliasCheck check = i.next();
+				if (check.check(path,resource))
+				{
+					if (LOG.isDebugEnabled())
+						LOG.debug("Aliased resource: " + resource + " approved by " + check);
+					return true;
+				}
+			}
+			return false;
+		}
+		return true;
+	}
+	
+	/* ------------------------------------------------------------ */
+	/**
+	 * Convert URL to Resource wrapper for {@link Resource#newResource(URL)} enables extensions to provide alternate resource implementations.
+	 */
+	public Resource newResource(URL url) throws IOException
+	{
+		return Resource.newResource(url);
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * Convert a URL or path to a Resource. The default implementation is a wrapper for {@link Resource#newResource(String)}.
+	 *
+	 * @param urlOrPath
+	 *            The URL or path to convert
+	 * @return The Resource for the URL/path
+	 * @throws IOException
+	 *             The Resource could not be created.
+	 */
+	public Resource newResource(String urlOrPath) throws IOException
+	{
+		return Resource.newResource(urlOrPath);
+	}
+
+	/* ------------------------------------------------------------ */
+	/*
+	 */
+	public Set<String> getResourcePaths(String path)
+	{
+		try
+		{
+			path = URIUtil.canonicalPath(path);
+			Resource resource = getResource(path);
+
+			if (resource != null && resource.exists())
+			{
+				if (!path.endsWith(URIUtil.SLASH))
+					path = path + URIUtil.SLASH;
+
+				String[] l = resource.list();
+				if (l != null)
+				{
+					HashSet<String> set = new HashSet<String>();
+					for (int i = 0; i < l.length; i++)
+						set.add(path + l[i]);
+					return set;
+				}
+			}
+		}
+		catch (Exception e)
+		{
+			LOG.ignore(e);
+		}
+		return Collections.emptySet();
+	}
+
+	/* ------------------------------------------------------------ */
+	private String normalizeHostname(String host)
+	{
+		if (host == null)
+			return null;
+
+		if (host.endsWith("."))
+			return host.substring(0,host.length() - 1);
+
+		return host;
+	}
+	
+	/* ------------------------------------------------------------ */
+	/**
+	 * Add an AliasCheck instance to possibly permit aliased resources
+	 * @param check The alias checker
+	 */
+	public void addAliasCheck(AliasCheck check)
+	{
+		_aliasChecks.add(check);
+	}
+	
+	/* ------------------------------------------------------------ */
+	/**
+	 * @return Mutable list of Alias checks
+	 */
+	public List<AliasCheck> getAliasChecks()
+	{
+		return _aliasChecks;
+	}
+
+	/* ------------------------------------------------------------ */
+	/**
+	 * Context.
+	 * <p>
+	 * A partial implementation of {@link javax.servlet.ServletContext}. A complete implementation is provided by the derived {@link ContextHandler}.
+	 * </p>
+	 *
+	 *
+	 */
+	public class Context implements ServletContext
+	{
+		protected int _majorVersion = 3;
+		protected int _minorVersion = 0;
+		protected boolean _enabled = true; //whether or not the dynamic API is enabled for callers
+
+		/* ------------------------------------------------------------ */
+		protected Context()
+		{
+		}
+
+		/* ------------------------------------------------------------ */
+		public ContextHandler getContextHandler()
+		{
+			// TODO reduce visibility of this method
+			return ContextHandler.this;
+		}
+
+		/* ------------------------------------------------------------ */
+		/*
+		 * @see javax.servlet.ServletContext#getContext(java.lang.String)
+		 */
+		@Override
+		public ServletContext getContext(String uripath)
+		{
+			List<ContextHandler> contexts = new ArrayList<ContextHandler>();
+			Handler[] handlers = getServer().getChildHandlersByClass(ContextHandler.class);
+			String matched_path = null;
+
+			for (Handler handler : handlers)
+			{
+				if (handler == null)
+					continue;
+				ContextHandler ch = (ContextHandler)handler;
+				String context_path = ch.getContextPath();
+
+				if (uripath.equals(context_path) || (uripath.startsWith(context_path) && uripath.charAt(context_path.length()) == '/')
+						|| "/".equals(context_path))
+				{
+					// look first for vhost matching context only
+					if (getVirtualHosts() != null && getVirtualHosts().length > 0)
+					{
+						if (ch.getVirtualHosts() != null && ch.getVirtualHosts().length > 0)
+						{
+							for (String h1 : getVirtualHosts())
+								for (String h2 : ch.getVirtualHosts())
+									if (h1.equals(h2))
+									{
+										if (matched_path == null || context_path.length() > matched_path.length())
+										{
+											contexts.clear();
+											matched_path = context_path;
+										}
+
+										if (matched_path.equals(context_path))
+											contexts.add(ch);
+									}
+						}
+					}
+					else
+					{
+						if (matched_path == null || context_path.length() > matched_path.length())
+						{
+							contexts.clear();
+							matched_path = context_path;
+						}
+
+						if (matched_path.equals(context_path))
+							contexts.add(ch);
+					}
+				}
+			}
+
+			if (contexts.size() > 0)
+				return contexts.get(0)._scontext;
+
+			// try again ignoring virtual hosts
+			matched_path = null;
+			for (Handler handler : handlers)
+			{
+				if (handler == null)
+					continue;
+				ContextHandler ch = (ContextHandler)handler;
+				String context_path = ch.getContextPath();
+
+				if (uripath.equals(context_path) || (uripath.startsWith(context_path) && uripath.charAt(context_path.length()) == '/')
+						|| "/".equals(context_path))
+				{
+					if (matched_path == null || context_path.length() > matched_path.length())
+					{
+						contexts.clear();
+						matched_path = context_path;
+					}
+
+					if (matched_path.equals(context_path))
+						contexts.add(ch);
+				}
+			}
+
+			if (contexts.size() > 0)
+				return contexts.get(0)._scontext;
+			return null;
+		}
+
+		/* ------------------------------------------------------------ */
+		/*
+		 * @see javax.servlet.ServletContext#getMajorVersion()
+		 */
+		@Override
+		public int getMajorVersion()
+		{
+			return 3;
+		}
+	  
+
+		/* ------------------------------------------------------------ */
+		/*
+		 * @see javax.servlet.ServletContext#getMimeType(java.lang.String)
+		 */
+		@Override
+		public String getMimeType(String file)
+		{
+			if (_mimeTypes == null)
+				return null;
+			Buffer mime = _mimeTypes.getMimeByExtension(file);
+			if (mime != null)
+				return mime.toString();
+			return null;
+		}
+
+		/* ------------------------------------------------------------ */
+		/*
+		 * @see javax.servlet.ServletContext#getMinorVersion()
+		 */
+		@Override
+		public int getMinorVersion()
+		{
+			return 0;
+		}
+
+		/* ------------------------------------------------------------ */
+		/*
+		 * @see javax.servlet.ServletContext#getNamedDispatcher(java.lang.String)
+		 */
+		@Override
+		public RequestDispatcher getNamedDispatcher(String name)
+		{
+			return null;
+		}
+
+		/* ------------------------------------------------------------ */
+		/*
+		 * @see javax.servlet.ServletContext#getRequestDispatcher(java.lang.String)
+		 */
+		@Override
+		public RequestDispatcher getRequestDispatcher(String uriInContext)
+		{
+			if (uriInContext == null)
+				return null;
+
+			if (!uriInContext.startsWith("/"))
+				return null;
+
+			try
+			{
+				String query = null;
+				int q = 0;
+				if ((q = uriInContext.indexOf('?')) > 0)
+				{
+					query = uriInContext.substring(q + 1);
+					uriInContext = uriInContext.substring(0,q);
+				}
+
+				String pathInContext = URIUtil.canonicalPath(URIUtil.decodePath(uriInContext));
+				if (pathInContext!=null)
+				{
+					String uri = URIUtil.addPaths(getContextPath(),uriInContext);
+					ContextHandler context = ContextHandler.this;
+					return new Dispatcher(context,uri,pathInContext,query);
+				}
+			}
+			catch (Exception e)
+			{
+				LOG.ignore(e);
+			}
+			return null;
+		}
+
+		/* ------------------------------------------------------------ */
+		/*
+		 * @see javax.servlet.ServletContext#getRealPath(java.lang.String)
+		 */
+		@Override
+		public String getRealPath(String path)
+		{
+			if (path == null)
+				return null;
+			if (path.length() == 0)
+				path = URIUtil.SLASH;
+			else if (path.charAt(0) != '/')
+				path = URIUtil.SLASH + path;
+
+			try
+			{
+				Resource resource = ContextHandler.this.getResource(path);
+				if (resource != null)
+				{
+					File file = resource.getFile();
+					if (file != null)
+						return file.getCanonicalPath();
+				}
+			}
+			catch (Exception e)
+			{
+				LOG.ignore(e);
+			}
+
+			return null;
+		}
+
+		/* ------------------------------------------------------------ */
+		@Override
+		public URL getResource(String path) throws MalformedURLException
+		{
+			Resource resource = ContextHandler.this.getResource(path);
+			if (resource != null && resource.exists())
+				return resource.getURL();
+			return null;
+		}
+
+		/* ------------------------------------------------------------ */
+		/*
+		 * @see javax.servlet.ServletContext#getResourceAsStream(java.lang.String)
+		 */
+		@Override
+		public InputStream getResourceAsStream(String path)
+		{
+			try
+			{
+				URL url = getResource(path);
+				if (url == null)
+					return null;
+				Resource r = Resource.newResource(url);
+				return r.getInputStream();
+			}
+			catch (Exception e)
+			{
+				LOG.ignore(e);
+				return null;
+			}
+		}
+
+		/* ------------------------------------------------------------ */
+		/*
+		 * @see javax.servlet.ServletContext#getResourcePaths(java.lang.String)
+		 */
+		@Override
+		public Set getResourcePaths(String path)
+		{
+			return ContextHandler.this.getResourcePaths(path);
+		}
+
+		/* ------------------------------------------------------------ */
+		/*
+		 * @see javax.servlet.ServletContext#getServerInfo()
+		 */
+		@Override
+		public String getServerInfo()
+		{
+			return "jetty/" + Server.getVersion();
+		}
+
+		/* ------------------------------------------------------------ */
+		/*
+		 * @see javax.servlet.ServletContext#getServlet(java.lang.String)
+		 */
+		@Override
+		@Deprecated
+		public Servlet getServlet(String name) throws ServletException
+		{
+			return null;
+		}
+
+		/* ------------------------------------------------------------ */
+		/*
+		 * @see javax.servlet.ServletContext#getServletNames()
+		 */
+		@SuppressWarnings("unchecked")
+		@Override
+		@Deprecated
+		public Enumeration getServletNames()
+		{
+			return Collections.enumeration(Collections.EMPTY_LIST);
+		}
+
+		/* ------------------------------------------------------------ */
+		/*
+		 * @see javax.servlet.ServletContext#getServlets()
+		 */
+		@SuppressWarnings("unchecked")
+		@Override
+		@Deprecated
+		public Enumeration getServlets()
+		{
+			return Collections.enumeration(Collections.EMPTY_LIST);
+		}
+
+		/* ------------------------------------------------------------ */
+		/*
+		 * @see javax.servlet.ServletContext#log(java.lang.Exception, java.lang.String)
+		 */
+		@Override
+		public void log(Exception exception, String msg)
+		{
+			_logger.warn(msg,exception);
+		}
+
+		/* ------------------------------------------------------------ */
+		/*
+		 * @see javax.servlet.ServletContext#log(java.lang.String)
+		 */
+		@Override
+		public void log(String msg)
+		{
+			_logger.info(msg);
+		}
+
+		/* ------------------------------------------------------------ */
+		/*
+		 * @see javax.servlet.ServletContext#log(java.lang.String, java.lang.Throwable)
+		 */
+		@Override
+		public void log(String message, Throwable throwable)
+		{
+			_logger.warn(message,throwable);
+		}
+
+		/* ------------------------------------------------------------ */
+		/*
+		 * @see javax.servlet.ServletContext#getInitParameter(java.lang.String)
+		 */
+		@Override
+		public String getInitParameter(String name)
+		{
+			return ContextHandler.this.getInitParameter(name);
+		}
+
+		/* ------------------------------------------------------------ */
+		/*
+		 * @see javax.servlet.ServletContext#getInitParameterNames()
+		 */
+		@SuppressWarnings("unchecked")
+		@Override
+		public Enumeration getInitParameterNames()
+		{
+			return ContextHandler.this.getInitParameterNames();
+		}
+
+		/* ------------------------------------------------------------ */
+		/*
+		 * @see javax.servlet.ServletContext#getAttribute(java.lang.String)
+		 */
+		@Override
+		public synchronized Object getAttribute(String name)
+		{
+			Object o = ContextHandler.this.getAttribute(name);
+			if (o == null && _contextAttributes != null)
+				o = _contextAttributes.getAttribute(name);
+			return o;
+		}
+
+		/* ------------------------------------------------------------ */
+		/*
+		 * @see javax.servlet.ServletContext#getAttributeNames()
+		 */
+		@SuppressWarnings("unchecked")
+		@Override
+		public synchronized Enumeration getAttributeNames()
+		{
+			HashSet<String> set = new HashSet<String>();
+			if (_contextAttributes != null)
+			{
+				Enumeration<String> e = _contextAttributes.getAttributeNames();
+				while (e.hasMoreElements())
+					set.add(e.nextElement());
+			}
+			Enumeration<String> e = _attributes.getAttributeNames();
+			while (e.hasMoreElements())
+				set.add(e.nextElement());
+
+			return Collections.enumeration(set);
+		}
+
+		/* ------------------------------------------------------------ */
+		/*
+		 * @see javax.servlet.ServletContext#setAttribute(java.lang.String, java.lang.Object)
+		 */
+		@Override
+		public synchronized void setAttribute(String name, Object value)
+		{
+			checkManagedAttribute(name,value);
+			Object old_value = _contextAttributes.getAttribute(name);
+
+			if (value == null)
+				_contextAttributes.removeAttribute(name);
+			else
+				_contextAttributes.setAttribute(name,value);
+
+			if (_contextAttributeListeners != null)
+			{
+				ServletContextAttributeEvent event = new ServletContextAttributeEvent(_scontext,name,old_value == null?value:old_value);
+
+				for (int i = 0; i < LazyList.size(_contextAttributeListeners); i++)
+				{
+					ServletContextAttributeListener l = (ServletContextAttributeListener)LazyList.get(_contextAttributeListeners,i);
+
+					if (old_value == null)
+						l.attributeAdded(event);
+					else if (value == null)
+						l.attributeRemoved(event);
+					else
+						l.attributeReplaced(event);
+				}
+			}
+		}
+
+		/* ------------------------------------------------------------ */
+		/*
+		 * @see javax.servlet.ServletContext#removeAttribute(java.lang.String)
+		 */
+		@Override
+		public synchronized void removeAttribute(String name)
+		{
+			checkManagedAttribute(name,null);
+
+			if (_contextAttributes == null)
+			{
+				// Set it on the handler
+				_attributes.removeAttribute(name);
+				return;
+			}
+
+			Object old_value = _contextAttributes.getAttribute(name);
+			_contextAttributes.removeAttribute(name);
+			if (old_value != null)
+			{
+				if (_contextAttributeListeners != null)
+				{
+					ServletContextAttributeEvent event = new ServletContextAttributeEvent(_scontext,name,old_value);
+
+					for (int i = 0; i < LazyList.size(_contextAttributeListeners); i++)
+						((ServletContextAttributeListener)LazyList.get(_contextAttributeListeners,i)).attributeRemoved(event);
+				}
+			}
+		}
+
+		/* ------------------------------------------------------------ */
+		/*
+		 * @see javax.servlet.ServletContext#getServletContextName()
+		 */
+		@Override
+		public String getServletContextName()
+		{
+			String name = ContextHandler.this.getDisplayName();
+			if (name == null)
+				name = ContextHandler.this.getContextPath();
+			return name;
+		}
+
+		/* ------------------------------------------------------------ */
+		@Override
+		public String getContextPath()
+		{
+			if ((_contextPath != null) && _contextPath.equals(URIUtil.SLASH))
+				return "";
+
+			return _contextPath;
+		}
+
+		/* ------------------------------------------------------------ */
+		@Override
+		public String toString()
+		{
+			return "ServletContext@" + ContextHandler.this.toString();
+		}
+
+		/* ------------------------------------------------------------ */
+		@Override
+		public boolean setInitParameter(String name, String value)
+		{
+			if (ContextHandler.this.getInitParameter(name) != null)
+				return false;
+			ContextHandler.this.getInitParams().put(name,value);
+			return true;
+		}
+
+		/* ------------------------------------------------------------ */
+		final private static String __unimplmented="Unimplemented - use org.eclipse.jetty.servlet.ServletContextHandler";
+
+		@Override
+		public Dynamic addFilter(String filterName, Class<? extends Filter> filterClass)
+		{
+			LOG.warn(__unimplmented);
+			return null;
+		}
+
+		@Override
+		public Dynamic addFilter(String filterName, Filter filter)
+		{
+			LOG.warn(__unimplmented);
+			return null;
+		}
+
+		@Override
+		public Dynamic addFilter(String filterName, String className)
+		{
+			LOG.warn(__unimplmented);
+			return null;
+		}
+
+		@Override
+		public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, Class<? extends Servlet> servletClass)
+		{
+			LOG.warn(__unimplmented);
+			return null;
+		}
+
+		@Override
+		public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet)
+		{
+			LOG.warn(__unimplmented);
+			return null;
+		}
+
+		@Override
+		public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, String className)
+		{
+			LOG.warn(__unimplmented);
+			return null;
+		}
+
+		@Override
+		public <T extends Filter> T createFilter(Class<T> c) throws ServletException
+		{
+			LOG.warn(__unimplmented);
+			return null;
+		}
+
+		@Override
+		public <T extends Servlet> T createServlet(Class<T> c) throws ServletException
+		{
+			LOG.warn(__unimplmented);
+			return null;
+		}
+
+		@Override
+		public Set<SessionTrackingMode> getDefaultSessionTrackingModes()
+		{
+			LOG.warn(__unimplmented);
+			return null;
+		}
+
+		@Override
+		public Set<SessionTrackingMode> getEffectiveSessionTrackingModes()
+		{
+			LOG.warn(__unimplmented);
+			return null;
+		}
+
+		@Override
+		public FilterRegistration getFilterRegistration(String filterName)
+		{
+			LOG.warn(__unimplmented);
+			return null;
+		}
+
+		@Override
+		public Map<String, ? extends FilterRegistration> getFilterRegistrations()
+		{
+			LOG.warn(__unimplmented);
+			return null;
+		}
+
+		@Override
+		public ServletRegistration getServletRegistration(String servletName)
+		{
+			LOG.warn(__unimplmented);
+			return null;
+		}
+
+		@Override
+		public Map<String, ? extends ServletRegistration> getServletRegistrations()
+		{
+			LOG.warn(__unimplmented);
+			return null;
+		}
+
+		@Override
+		public SessionCookieConfig getSessionCookieConfig()
+		{
+			LOG.warn(__unimplmented);
+			return null;
+		}
+
+		@Override
+		public void setSessionTrackingModes(Set<SessionTrackingMode> sessionTrackingModes)
+		{
+			LOG.warn(__unimplmented);
+		}
+
+		@Override
+		public void addListener(String className)
+		{
+			if (!_enabled)
+				throw new UnsupportedOperationException();
+			
+			try
+			{
+//				Class<? extends EventListener> clazz = _classLoader==null?Loader.loadClass(ContextHandler.class,className):_classLoader.loadClass(className);
+				Class clazz = _classLoader==null?Loader.loadClass(ContextHandler.class,className):_classLoader.loadClass(className);
+				addListener(clazz);
+			}
+			catch (ClassNotFoundException e)
+			{
+				throw new IllegalArgumentException(e);
+			}
+		}
+
+		@Override
+		public <T extends EventListener> void addListener(T t)
+		{            
+			if (!_enabled)
+				throw new UnsupportedOperationException();
+			ContextHandler.this.addEventListener(t);
+			ContextHandler.this.restrictEventListener(t);
+		}
+
+		@Override
+		public void addListener(Class<? extends EventListener> listenerClass)
+		{            
+			if (!_enabled)
+				throw new UnsupportedOperationException();
+
+			try
+			{
+				EventListener e = createListener(listenerClass);
+				ContextHandler.this.addEventListener(e);
+				ContextHandler.this.restrictEventListener(e);
+			}
+			catch (ServletException e)
+			{
+				throw new IllegalArgumentException(e);
+			}
+		}
+
+		@Override
+		public <T extends EventListener> T createListener(Class<T> clazz) throws ServletException
+		{
+			try
+			{
+				return clazz.newInstance();
+			}
+			catch (InstantiationException e)
+			{
+				throw new ServletException(e);
+			}
+			catch (IllegalAccessException e)
+			{
+				throw new ServletException(e);
+			}
+		}
+
+		@Override
+		public ClassLoader getClassLoader()
+		{
+			AccessController.checkPermission(new RuntimePermission("getClassLoader"));
+			return _classLoader;
+		}
+
+		@Override
+		public int getEffectiveMajorVersion()
+		{
+			return _majorVersion;
+		}
+
+		@Override
+		public int getEffectiveMinorVersion()
+		{
+			return _minorVersion;
+		}
+
+		public void setEffectiveMajorVersion (int v)
+		{
+			_majorVersion = v;
+		}
+		
+		public void setEffectiveMinorVersion (int v)
+		{
+			_minorVersion = v;
+		}
+		
+		@Override
+		public JspConfigDescriptor getJspConfigDescriptor()
+		{
+			LOG.warn(__unimplmented);
+			return null;
+		}
+
+		public void setJspConfigDescriptor(JspConfigDescriptor d)
+		{
+			
+		}
+		
+		@Override
+		public void declareRoles(String... roleNames)
+		{
+			if (!isStarting())
+				throw new IllegalStateException ();
+			if (!_enabled)
+				throw new UnsupportedOperationException();
+			
+			// TODO Auto-generated method stub
+			
+		}
+
+		public void setEnabled(boolean enabled)
+		{
+			_enabled = enabled;
+		}
+
+		public boolean isEnabled()
+		{
+			return _enabled;
+		}
+	}
+
+	private static class CLDump implements Dumpable
+	{
+		final ClassLoader _loader;
+
+		CLDump(ClassLoader loader)
+		{
+			_loader = loader;
+		}
+
+		public String dump()
+		{
+			return AggregateLifeCycle.dump(this);
+		}
+
+		public void dump(Appendable out, String indent) throws IOException
+		{
+			out.append(String.valueOf(_loader)).append("\n");
+
+			if (_loader != null)
+			{
+				Object parent = _loader.getParent();
+				if (parent != null)
+				{
+					if (!(parent instanceof Dumpable))
+						parent = new CLDump((ClassLoader)parent);
+
+					if (_loader instanceof URLClassLoader)
+						AggregateLifeCycle.dump(out,indent,TypeUtil.asList(((URLClassLoader)_loader).getURLs()),Collections.singleton(parent));
+					else
+						AggregateLifeCycle.dump(out,indent,Collections.singleton(parent));
+				}
+			}
+		}
+	}
+	
+	
+	/* ------------------------------------------------------------ */
+	/** Interface to check aliases
+	 */
+	public interface AliasCheck
+	{
+		/* ------------------------------------------------------------ */
+		/** Check an alias
+		 * @param path The path the aliased resource was created for
+		 * @param resource The aliased resourced
+		 * @return True if the resource is OK to be served.
+		 */
+		boolean check(String path, Resource resource);
+	}
+	
+	
+	/* ------------------------------------------------------------ */
+	/** Approve Aliases with same suffix.
+	 * Eg. a symbolic link from /foobar.html to /somewhere/wibble.html would be
+	 * approved because both the resource and alias end with ".html".
+	 */
+	@Deprecated
+	public static class ApproveSameSuffixAliases implements AliasCheck
+	{
+		{
+			LOG.warn("ApproveSameSuffixAlias is not safe for production");
+		}
+		
+		public boolean check(String path, Resource resource)
+		{
+			int dot = path.lastIndexOf('.');
+			if (dot<0)
+				return false;
+			String suffix=path.substring(dot);
+			return resource.toString().endsWith(suffix);
+		}
+	}
+	
+	
+	/* ------------------------------------------------------------ */
+	/** Approve Aliases with a path prefix.
+	 * Eg. a symbolic link from /dirA/foobar.html to /dirB/foobar.html would be
+	 * approved because both the resource and alias end with "/foobar.html".
+	 */
+	@Deprecated
+	public static class ApprovePathPrefixAliases implements AliasCheck
+	{
+		{
+			LOG.warn("ApprovePathPrefixAliases is not safe for production");
+		}
+		
+		public boolean check(String path, Resource resource)
+		{
+			int slash = path.lastIndexOf('/');
+			if (slash<0 || slash==path.length()-1)
+				return false;
+			String suffix=path.substring(slash);
+			return resource.toString().endsWith(suffix);
+		}
+	}
+	
+	/* ------------------------------------------------------------ */
+	/** Approve Aliases of a non existent directory.
+	 * If a directory "/foobar/" does not exist, then the resource is 
+	 * aliased to "/foobar".  Accept such aliases.
+	 */
+	public static class ApproveNonExistentDirectoryAliases implements AliasCheck
+	{
+		public boolean check(String path, Resource resource)
+		{
+			if (resource.exists())
+				return false;
+			
+			String a=resource.getAlias().toString();
+			String r=resource.getURL().toString();
+			
+			if (a.length()>r.length())
+				return a.startsWith(r) && a.length()==r.length()+1 && a.endsWith("/");
+			else
+				return r.startsWith(a) && r.length()==a.length()+1 && r.endsWith("/");
+		}
+	}
+	
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/handler/ContextHandlerCollection.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,332 @@
+//
+//  ========================================================================
+//  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.server.handler;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.PathMap;
+import org.eclipse.jetty.server.AsyncContinuation;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.HandlerContainer;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.util.LazyList;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/* ------------------------------------------------------------ */
+/** ContextHandlerCollection.
+ * 
+ * This {@link org.eclipse.jetty.server.handler.HandlerCollection} is creates a 
+ * {@link org.eclipse.jetty.http.PathMap} to it's contained handlers based
+ * on the context path and virtual hosts of any contained {@link org.eclipse.jetty.server.handler.ContextHandler}s.
+ * The contexts do not need to be directly contained, only children of the contained handlers.
+ * Multiple contexts may have the same context path and they are called in order until one
+ * handles the request.  
+ * 
+ * @org.apache.xbean.XBean element="contexts"
+ */
+public class ContextHandlerCollection extends HandlerCollection
+{
+    private static final Logger LOG = Log.getLogger(ContextHandlerCollection.class);
+ 
+    private volatile PathMap _contextMap;
+    private Class<? extends ContextHandler> _contextClass = ContextHandler.class;
+    
+    /* ------------------------------------------------------------ */
+    public ContextHandlerCollection()
+    {
+        super(true);
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Remap the context paths.
+     */
+    public void mapContexts()
+    {
+        PathMap contextMap = new PathMap();
+        Handler[] branches = getHandlers();
+        
+        
+        for (int b=0;branches!=null && b<branches.length;b++)
+        {
+            Handler[] handlers=null;
+            
+            if (branches[b] instanceof ContextHandler)
+            {
+                handlers = new Handler[]{ branches[b] };
+            }
+            else if (branches[b] instanceof HandlerContainer)
+            {
+                handlers = ((HandlerContainer)branches[b]).getChildHandlersByClass(ContextHandler.class);
+            }
+            else 
+                continue;
+            
+            for (int i=0;i<handlers.length;i++)
+            {
+                ContextHandler handler=(ContextHandler)handlers[i];
+
+                String contextPath=handler.getContextPath();
+
+                if (contextPath==null || contextPath.indexOf(',')>=0 || contextPath.startsWith("*"))
+                    throw new IllegalArgumentException ("Illegal context spec:"+contextPath);
+
+                if(!contextPath.startsWith("/"))
+                    contextPath='/'+contextPath;
+
+                if (contextPath.length()>1)
+                {
+                    if (contextPath.endsWith("/"))
+                        contextPath+="*";
+                    else if (!contextPath.endsWith("/*"))
+                        contextPath+="/*";
+                }
+
+                Object contexts=contextMap.get(contextPath);
+                String[] vhosts=handler.getVirtualHosts();
+
+                
+                if (vhosts!=null && vhosts.length>0)
+                {
+                    Map hosts;
+
+                    if (contexts instanceof Map)
+                        hosts=(Map)contexts;
+                    else
+                    {
+                        hosts=new HashMap(); 
+                        hosts.put("*",contexts);
+                        contextMap.put(contextPath, hosts);
+                    }
+
+                    for (int j=0;j<vhosts.length;j++)
+                    {
+                        String vhost=vhosts[j];
+                        contexts=hosts.get(vhost);
+                        contexts=LazyList.add(contexts,branches[b]);
+                        hosts.put(vhost,contexts);
+                    }
+                }
+                else if (contexts instanceof Map)
+                {
+                    Map hosts=(Map)contexts;
+                    contexts=hosts.get("*");
+                    contexts= LazyList.add(contexts, branches[b]);
+                    hosts.put("*",contexts);
+                }
+                else
+                {
+                    contexts= LazyList.add(contexts, branches[b]);
+                    contextMap.put(contextPath, contexts);
+                }
+            }
+        }
+        _contextMap=contextMap;
+
+    }
+    
+
+    
+    /* ------------------------------------------------------------ */
+    /* 
+     * @see org.eclipse.jetty.server.server.handler.HandlerCollection#setHandlers(org.eclipse.jetty.server.server.Handler[])
+     */
+    @Override
+    public void setHandlers(Handler[] handlers)
+    {
+        _contextMap=null;
+        super.setHandlers(handlers);
+        if (isStarted())
+            mapContexts();
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    protected void doStart() throws Exception
+    {
+        mapContexts();
+        super.doStart();
+    }
+    
+
+    /* ------------------------------------------------------------ */
+    /* 
+     * @see org.eclipse.jetty.server.server.Handler#handle(java.lang.String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int)
+     */
+    @Override
+    public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+    {
+        Handler[] handlers = getHandlers();
+        if (handlers==null || handlers.length==0)
+	    return;
+	
+	AsyncContinuation async = baseRequest.getAsyncContinuation();
+	if (async.isAsync())
+	{
+	    ContextHandler context=async.getContextHandler();
+	    if (context!=null)
+	    {
+	        context.handle(target,baseRequest,request, response);
+	        return;
+	    }
+	}
+	
+	// data structure which maps a request to a context; first-best match wins
+	// { context path => 
+	//     { virtual host => context } 
+	// }
+	PathMap map = _contextMap;
+	if (map!=null && target!=null && target.startsWith("/"))
+	{
+	    // first, get all contexts matched by context path
+	    Object contexts = map.getLazyMatches(target);
+
+            for (int i=0; i<LazyList.size(contexts); i++)
+            {
+                // then, match against the virtualhost of each context
+                Map.Entry entry = (Map.Entry)LazyList.get(contexts, i);
+                Object list = entry.getValue();
+
+                if (list instanceof Map)
+                {
+                    Map hosts = (Map)list;
+                    String host = normalizeHostname(request.getServerName());
+           
+                    // explicitly-defined virtual hosts, most specific
+                    list=hosts.get(host);
+                    for (int j=0; j<LazyList.size(list); j++)
+                    {
+                        Handler handler = (Handler)LazyList.get(list,j);
+                        handler.handle(target,baseRequest, request, response);
+                        if (baseRequest.isHandled())
+                            return;
+                    }
+                    
+                    // wildcard for one level of names 
+                    list=hosts.get("*."+host.substring(host.indexOf(".")+1));
+                    for (int j=0; j<LazyList.size(list); j++)
+                    {
+                        Handler handler = (Handler)LazyList.get(list,j);
+                        handler.handle(target,baseRequest, request, response);
+                        if (baseRequest.isHandled())
+                            return;
+                    }
+                    
+                    // no virtualhosts defined for the context, least specific
+                    // will handle any request that does not match to a specific virtual host above
+                    list=hosts.get("*");
+                    for (int j=0; j<LazyList.size(list); j++)
+                    {
+                        Handler handler = (Handler)LazyList.get(list,j);
+                        handler.handle(target,baseRequest, request, response);
+                        if (baseRequest.isHandled())
+                            return;
+                    }
+                }
+                else
+                {
+                    for (int j=0; j<LazyList.size(list); j++)
+                    {
+                        Handler handler = (Handler)LazyList.get(list,j);
+                        handler.handle(target,baseRequest, request, response);
+                        if (baseRequest.isHandled())
+                            return;
+                    }
+                }
+	    }
+	}
+	else
+	{
+            // This may not work in all circumstances... but then I think it should never be called
+	    for (int i=0;i<handlers.length;i++)
+	    {
+		handlers[i].handle(target,baseRequest, request, response);
+		if ( baseRequest.isHandled())
+		    return;
+	    }
+	}
+    }
+    
+    
+    /* ------------------------------------------------------------ */
+    /** Add a context handler.
+     * @param contextPath  The context path to add
+     * @return the ContextHandler just added
+     */
+    public ContextHandler addContext(String contextPath,String resourceBase) 
+    {
+        try
+        {
+            ContextHandler context = _contextClass.newInstance();
+            context.setContextPath(contextPath);
+            context.setResourceBase(resourceBase);
+            addHandler(context);
+            return context;
+        }
+        catch (Exception e)
+        {
+            LOG.debug(e);
+            throw new Error(e);
+        }
+    }
+
+
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The class to use to add new Contexts
+     */
+    public Class getContextClass()
+    {
+        return _contextClass;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param contextClass The class to use to add new Contexts
+     */
+    public void setContextClass(Class contextClass)
+    {
+        if (contextClass ==null || !(ContextHandler.class.isAssignableFrom(contextClass)))
+            throw new IllegalArgumentException();
+        _contextClass = contextClass;
+    }
+    
+    /* ------------------------------------------------------------ */
+    private String normalizeHostname( String host )
+    {
+        if ( host == null )
+            return null;
+        
+        if ( host.endsWith( "." ) )
+            return host.substring( 0, host.length() -1);
+      
+        return host;
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/handler/DebugHandler.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,158 @@
+//
+//  ========================================================================
+//  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.server.handler; 
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.Locale;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.util.DateCache;
+import org.eclipse.jetty.util.RolloverFileOutputStream;
+
+
+/** 
+ * Debug Handler.
+ * A lightweight debug handler that can be used in production code.
+ * Details of the request and response are written to an output stream
+ * and the current thread name is updated with information that will link
+ * to the details in that output.
+ */
+public class DebugHandler extends HandlerWrapper
+{
+    private DateCache _date=new DateCache("HH:mm:ss", Locale.US);
+    private OutputStream _out;
+    private PrintStream _print;
+    
+    /* ------------------------------------------------------------ */
+    /* 
+     * @see org.eclipse.jetty.server.Handler#handle(java.lang.String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int)
+     */
+    @Override
+    public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
+            throws IOException, ServletException
+    {
+        final Response base_response = baseRequest.getResponse();
+        final Thread thread=Thread.currentThread();
+        final String old_name=thread.getName();
+
+        boolean suspend=false;
+        boolean retry=false;
+        String name=(String)request.getAttribute("org.eclipse.jetty.thread.name");
+        if (name==null)
+            name=old_name+":"+baseRequest.getScheme()+"://"+baseRequest.getLocalAddr()+":"+baseRequest.getLocalPort()+baseRequest.getUri();
+        else
+            retry=true;
+        
+        String ex=null;
+        try
+        {
+            final String d=_date.now();
+            final int ms=_date.lastMs();
+            
+            if (retry)
+                _print.println(d+(ms>99?".":(ms>9?".0":".00"))+ms+":"+name+" RETRY");
+            else
+                _print.println(d+(ms>99?".":(ms>9?".0":".00"))+ms+":"+name+" "+baseRequest.getRemoteAddr()+" "+request.getMethod()+" "+baseRequest.getHeader("Cookie")+"; "+baseRequest.getHeader("User-Agent"));
+            thread.setName(name);
+            
+            getHandler().handle(target,baseRequest,request,response);
+        }
+        catch(IOException ioe)
+        {
+            ex=ioe.toString();
+            throw ioe;
+        }
+        catch(ServletException se)
+        {
+            ex=se.toString()+":"+se.getCause();
+            throw se;
+        }
+        catch(RuntimeException rte)
+        {
+            ex=rte.toString();
+            throw rte;
+        }
+        catch(Error e)
+        {
+            ex=e.toString();
+            throw e;
+        }
+        finally
+        {
+            thread.setName(old_name);
+            final String d=_date.now();
+            final int ms=_date.lastMs();
+            suspend=baseRequest.getAsyncContinuation().isSuspended();
+            if (suspend)
+            {
+                request.setAttribute("org.eclipse.jetty.thread.name",name);
+                _print.println(d+(ms>99?".":(ms>9?".0":".00"))+ms+":"+name+" SUSPEND");
+            }
+            else
+                _print.println(d+(ms>99?".":(ms>9?".0":".00"))+ms+":"+name+" "+base_response.getStatus()+
+		        (ex==null?"":("/"+ex))+
+		        " "+base_response.getContentType()+" "+base_response.getContentCount());
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jetty.server.handler.HandlerWrapper#doStart()
+     */
+    @Override
+    protected void doStart() throws Exception
+    {
+        if (_out==null)
+            _out=new RolloverFileOutputStream("./logs/yyyy_mm_dd.debug.log",true);
+        _print=new PrintStream(_out);
+        super.doStart();
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.jetty.server.handler.HandlerWrapper#doStop()
+     */
+    @Override
+    protected void doStop() throws Exception
+    {
+        super.doStop();
+        _print.close();
+    }
+
+    /**
+     * @return the out
+     */
+    public OutputStream getOutputStream()
+    {
+        return _out;
+    }
+
+    /**
+     * @param out the out to set
+     */
+    public void setOutputStream(OutputStream out)
+    {
+        _out = out;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/handler/DefaultHandler.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,208 @@
+//
+//  ========================================================================
+//  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.server.handler;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URL;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.HttpHeaders;
+import org.eclipse.jetty.http.HttpMethods;
+import org.eclipse.jetty.http.MimeTypes;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.util.ByteArrayISO8859Writer;
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.resource.Resource;
+
+
+/* ------------------------------------------------------------ */
+/** Default Handler.
+ * 
+ * This handle will deal with unhandled requests in the server.
+ * For requests for favicon.ico, the Jetty icon is served. 
+ * For reqests to '/' a 404 with a list of known contexts is served.
+ * For all other requests a normal 404 is served.
+ * TODO Implement OPTIONS and TRACE methods for the server.
+ * 
+ * 
+ * @org.apache.xbean.XBean
+ */
+public class DefaultHandler extends AbstractHandler
+{
+    private static final Logger LOG = Log.getLogger(DefaultHandler.class);
+
+    final long _faviconModified=(System.currentTimeMillis()/1000)*1000L;
+    byte[] _favicon;
+    boolean _serveIcon=true;
+    boolean _showContexts=true;
+    
+    public DefaultHandler()
+    {
+        try
+        {
+            URL fav = this.getClass().getClassLoader().getResource("org/eclipse/jetty/favicon.ico");
+            if (fav!=null)
+            {
+                Resource r = Resource.newResource(fav);
+                _favicon=IO.readBytes(r.getInputStream());
+            }
+        }
+        catch(Exception e)
+        {
+            LOG.warn(e);
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    /* 
+     * @see org.eclipse.jetty.server.server.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int)
+     */
+    public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+    {              
+        if (response.isCommitted() || baseRequest.isHandled())
+            return;
+        
+        baseRequest.setHandled(true);
+        
+        String method=request.getMethod();
+
+        // little cheat for common request
+        if (_serveIcon && _favicon!=null && method.equals(HttpMethods.GET) && request.getRequestURI().equals("/favicon.ico"))
+        {
+            if (request.getDateHeader(HttpHeaders.IF_MODIFIED_SINCE)==_faviconModified)
+                response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
+            else
+            {
+                response.setStatus(HttpServletResponse.SC_OK);
+                response.setContentType("image/x-icon");
+                response.setContentLength(_favicon.length);
+                response.setDateHeader(HttpHeaders.LAST_MODIFIED, _faviconModified);
+                response.setHeader(HttpHeaders.CACHE_CONTROL,"max-age=360000,public");
+                response.getOutputStream().write(_favicon);
+            }
+            return;
+        }
+        
+        
+        if (!method.equals(HttpMethods.GET) || !request.getRequestURI().equals("/"))
+        {
+            response.sendError(HttpServletResponse.SC_NOT_FOUND);
+            return;   
+        }
+
+        response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+        response.setContentType(MimeTypes.TEXT_HTML);
+        
+        ByteArrayISO8859Writer writer = new ByteArrayISO8859Writer(1500);
+        
+        writer.write("<HTML>\n<HEAD>\n<TITLE>Error 404 - Not Found");
+        writer.write("</TITLE>\n<BODY>\n<H2>Error 404 - Not Found.</H2>\n");
+        writer.write("No context on this server matched or handled this request.<BR>");
+        
+        if (_showContexts)
+        {
+            writer.write("Contexts known to this server are: <ul>");
+            
+            Server server = getServer();
+            Handler[] handlers = server==null?null:server.getChildHandlersByClass(ContextHandler.class);
+     
+            for (int i=0;handlers!=null && i<handlers.length;i++)
+            {
+                ContextHandler context = (ContextHandler)handlers[i];
+                if (context.isRunning())
+                {
+                    writer.write("<li><a href=\"");
+                    if (context.getVirtualHosts()!=null && context.getVirtualHosts().length>0)
+                        writer.write("http://"+context.getVirtualHosts()[0]+":"+request.getLocalPort());
+                    writer.write(context.getContextPath());
+                    if (context.getContextPath().length()>1 && context.getContextPath().endsWith("/"))
+                        writer.write("/");
+                    writer.write("\">");
+                    writer.write(context.getContextPath());
+                    if (context.getVirtualHosts()!=null && context.getVirtualHosts().length>0)
+                        writer.write("&nbsp;@&nbsp;"+context.getVirtualHosts()[0]+":"+request.getLocalPort());
+                    writer.write("&nbsp;--->&nbsp;");
+                    writer.write(context.toString());
+                    writer.write("</a></li>\n");
+                }
+                else
+                {
+                    writer.write("<li>");
+                    writer.write(context.getContextPath());
+                    if (context.getVirtualHosts()!=null && context.getVirtualHosts().length>0)
+                        writer.write("&nbsp;@&nbsp;"+context.getVirtualHosts()[0]+":"+request.getLocalPort());
+                    writer.write("&nbsp;--->&nbsp;");
+                    writer.write(context.toString());
+                    if (context.isFailed())
+                        writer.write(" [failed]");
+                    if (context.isStopped())
+                        writer.write(" [stopped]");
+                    writer.write("</li>\n");
+                }
+            }
+        }
+        
+        for (int i=0;i<10;i++)
+            writer.write("\n<!-- Padding for IE                  -->");
+        
+        writer.write("\n</BODY>\n</HTML>\n");
+        writer.flush();
+        response.setContentLength(writer.size());
+        OutputStream out=response.getOutputStream();
+        writer.writeTo(out);
+        out.close();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns true if the handle can server the jetty favicon.ico
+     */
+    public boolean getServeIcon()
+    {
+        return _serveIcon;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param serveIcon true if the handle can server the jetty favicon.ico
+     */
+    public void setServeIcon(boolean serveIcon)
+    {
+        _serveIcon = serveIcon;
+    }
+    
+    public boolean getShowContexts()
+    {
+        return _showContexts;
+    }
+
+    public void setShowContexts(boolean show)
+    {
+        _showContexts = show;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/handler/ErrorHandler.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,285 @@
+//
+//  ========================================================================
+//  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.server.handler;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.HttpHeaders;
+import org.eclipse.jetty.http.HttpMethods;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.MimeTypes;
+import org.eclipse.jetty.server.AbstractHttpConnection;
+import org.eclipse.jetty.server.Dispatcher;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.util.ByteArrayISO8859Writer;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/* ------------------------------------------------------------ */
+/** Handler for Error pages
+ * An ErrorHandler is registered with {@link ContextHandler#setErrorHandler(ErrorHandler)} or 
+ * {@link org.eclipse.jetty.server.Server#addBean(Object)}.   
+ * It is called by the HttpResponse.sendError method to write a error page.
+ * 
+ */
+public class ErrorHandler extends AbstractHandler
+{    
+    private static final Logger LOG = Log.getLogger(ErrorHandler.class);
+    public final static String ERROR_PAGE="org.eclipse.jetty.server.error_page";
+    
+    boolean _showStacks=true;
+    boolean _showMessageInTitle=true;
+    String _cacheControl="must-revalidate,no-cache,no-store";
+    
+    /* ------------------------------------------------------------ */
+    /* 
+     * @see org.eclipse.jetty.server.server.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int)
+     */
+    public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
+    {
+        AbstractHttpConnection connection = AbstractHttpConnection.getCurrentConnection();
+        String method = request.getMethod();
+        if(!method.equals(HttpMethods.GET) && !method.equals(HttpMethods.POST) && !method.equals(HttpMethods.HEAD))
+        {
+            connection.getRequest().setHandled(true);
+            return;
+        }
+        
+        if (this instanceof ErrorPageMapper)
+        {
+            String error_page=((ErrorPageMapper)this).getErrorPage(request);
+            if (error_page!=null && request.getServletContext()!=null)
+            {
+                String old_error_page=(String)request.getAttribute(ERROR_PAGE);
+                if (old_error_page==null || !old_error_page.equals(error_page))
+                {
+                    request.setAttribute(ERROR_PAGE, error_page);
+
+                    Dispatcher dispatcher = (Dispatcher) request.getServletContext().getRequestDispatcher(error_page);
+                    try
+                    {
+                        if(dispatcher!=null)
+                        {
+                            dispatcher.error(request, response);
+                            return;
+                        }
+                        LOG.warn("No error page "+error_page);
+                    }
+                    catch (ServletException e)
+                    {
+                        LOG.warn(Log.EXCEPTION, e);
+                        return;
+                    }
+                }
+            }
+        }
+        
+        connection.getRequest().setHandled(true);
+        response.setContentType(MimeTypes.TEXT_HTML_8859_1);    
+        if (_cacheControl!=null)
+            response.setHeader(HttpHeaders.CACHE_CONTROL, _cacheControl);
+        ByteArrayISO8859Writer writer= new ByteArrayISO8859Writer(4096);
+        handleErrorPage(request, writer, connection.getResponse().getStatus(), connection.getResponse().getReason());
+        writer.flush();
+        response.setContentLength(writer.size());
+        writer.writeTo(response.getOutputStream());
+        writer.destroy();
+    }
+
+    /* ------------------------------------------------------------ */
+    protected void handleErrorPage(HttpServletRequest request, Writer writer, int code, String message)
+        throws IOException
+    {
+        writeErrorPage(request, writer, code, message, _showStacks);
+    }
+    
+    /* ------------------------------------------------------------ */
+    protected void writeErrorPage(HttpServletRequest request, Writer writer, int code, String message, boolean showStacks)
+        throws IOException
+    {
+        if (message == null)
+            message=HttpStatus.getMessage(code);
+
+        writer.write("<html>\n<head>\n");
+        writeErrorPageHead(request,writer,code,message);
+        writer.write("</head>\n<body>");
+        writeErrorPageBody(request,writer,code,message,showStacks);
+        writer.write("\n</body>\n</html>\n");
+    }
+
+    /* ------------------------------------------------------------ */
+    protected void writeErrorPageHead(HttpServletRequest request, Writer writer, int code, String message)
+        throws IOException
+        {
+        writer.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=ISO-8859-1\"/>\n");
+        writer.write("<title>Error ");
+        writer.write(Integer.toString(code));
+
+        if (_showMessageInTitle)
+        {
+            writer.write(' ');
+            write(writer,message);
+        }
+        writer.write("</title>\n");    
+    }
+
+    /* ------------------------------------------------------------ */
+    protected void writeErrorPageBody(HttpServletRequest request, Writer writer, int code, String message, boolean showStacks)
+        throws IOException
+    {
+        String uri= request.getRequestURI();
+        
+        writeErrorPageMessage(request,writer,code,message,uri);
+        if (showStacks)
+            writeErrorPageStacks(request,writer);
+        writer.write("<hr /><i><small>Powered by Jetty://</small></i>");
+        for (int i= 0; i < 20; i++)
+            writer.write("<br/>                                                \n");
+    }
+
+    /* ------------------------------------------------------------ */
+    protected void writeErrorPageMessage(HttpServletRequest request, Writer writer, int code, String message,String uri)
+    throws IOException
+    {
+        writer.write("<h2>HTTP ERROR ");
+        writer.write(Integer.toString(code));
+        writer.write("</h2>\n<p>Problem accessing ");
+        write(writer,uri);
+        writer.write(". Reason:\n<pre>    ");
+        write(writer,message);
+        writer.write("</pre></p>");
+    }
+
+    /* ------------------------------------------------------------ */
+    protected void writeErrorPageStacks(HttpServletRequest request, Writer writer)
+        throws IOException
+    {
+        Throwable th = (Throwable)request.getAttribute("javax.servlet.error.exception");
+        while(th!=null)
+        {
+            writer.write("<h3>Caused by:</h3><pre>");
+            StringWriter sw = new StringWriter();
+            PrintWriter pw = new PrintWriter(sw);
+            th.printStackTrace(pw);
+            pw.flush();
+            write(writer,sw.getBuffer().toString());
+            writer.write("</pre>\n");
+
+            th =th.getCause();
+        }
+    }
+        
+
+    /* ------------------------------------------------------------ */
+    /** Get the cacheControl.
+     * @return the cacheControl header to set on error responses.
+     */
+    public String getCacheControl()
+    {
+        return _cacheControl;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set the cacheControl.
+     * @param cacheControl the cacheControl header to set on error responses.
+     */
+    public void setCacheControl(String cacheControl)
+    {
+        _cacheControl = cacheControl;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return True if stack traces are shown in the error pages
+     */
+    public boolean isShowStacks()
+    {
+        return _showStacks;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param showStacks True if stack traces are shown in the error pages
+     */
+    public void setShowStacks(boolean showStacks)
+    {
+        _showStacks = showStacks;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @param showMessageInTitle if true, the error message appears in page title
+     */
+    public void setShowMessageInTitle(boolean showMessageInTitle)
+    {
+        _showMessageInTitle = showMessageInTitle;
+    }
+    
+    
+    /* ------------------------------------------------------------ */
+    public boolean getShowMessageInTitle()
+    {
+        return _showMessageInTitle;
+    }
+
+    /* ------------------------------------------------------------ */
+    protected void write(Writer writer,String string)
+        throws IOException
+    {
+        if (string==null)
+            return;
+        
+        for (int i=0;i<string.length();i++)
+        {
+            char c=string.charAt(i);
+            
+            switch(c)
+            {
+                case '&' :
+                    writer.write("&amp;");
+                    break;
+                case '<' :
+                    writer.write("&lt;");
+                    break;
+                case '>' :
+                    writer.write("&gt;");
+                    break;
+                    
+                default:
+                    if (Character.isISOControl(c) && !Character.isWhitespace(c))
+                        writer.write('?');
+                    else 
+                        writer.write(c);
+            }          
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public interface ErrorPageMapper
+    {
+        String getErrorPage(HttpServletRequest request);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/handler/GzipHandler.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,356 @@
+//
+//  ========================================================================
+//  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.server.handler;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.zip.DeflaterOutputStream;
+import java.util.zip.GZIPOutputStream;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.continuation.Continuation;
+import org.eclipse.jetty.continuation.ContinuationListener;
+import org.eclipse.jetty.continuation.ContinuationSupport;
+import org.eclipse.jetty.http.HttpMethods;
+import org.eclipse.jetty.http.gzip.CompressedResponseWrapper;
+import org.eclipse.jetty.http.gzip.AbstractCompressedStream;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/* ------------------------------------------------------------ */
+/**
+ * GZIP Handler This handler will gzip the content of a response if:
+ * <ul>
+ * <li>The filter is mapped to a matching path</li>
+ * <li>The response status code is >=200 and <300
+ * <li>The content length is unknown or more than the <code>minGzipSize</code> initParameter or the minGzipSize is 0(default)</li>
+ * <li>The content-type is in the comma separated list of mimeTypes set in the <code>mimeTypes</code> initParameter or if no mimeTypes are defined the
+ * content-type is not "application/gzip"</li>
+ * <li>No content-encoding is specified by the resource</li>
+ * </ul>
+ * 
+ * <p>
+ * Compressing the content can greatly improve the network bandwidth usage, but at a cost of memory and CPU cycles. If this handler is used for static content,
+ * then use of efficient direct NIO may be prevented, thus use of the gzip mechanism of the <code>org.eclipse.jetty.servlet.DefaultServlet</code> is advised instead.
+ * </p>
+ */
+public class GzipHandler extends HandlerWrapper
+{
+    private static final Logger LOG = Log.getLogger(GzipHandler.class);
+
+    protected Set<String> _mimeTypes;
+    protected Set<String> _excluded;
+    protected int _bufferSize = 8192;
+    protected int _minGzipSize = 256;
+    protected String _vary = "Accept-Encoding, User-Agent";
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Instantiates a new gzip handler.
+     */
+    public GzipHandler()
+    {
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Get the mime types.
+     * 
+     * @return mime types to set
+     */
+    public Set<String> getMimeTypes()
+    {
+        return _mimeTypes;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set the mime types.
+     * 
+     * @param mimeTypes
+     *            the mime types to set
+     */
+    public void setMimeTypes(Set<String> mimeTypes)
+    {
+        _mimeTypes = mimeTypes;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set the mime types.
+     * 
+     * @param mimeTypes
+     *            the mime types to set
+     */
+    public void setMimeTypes(String mimeTypes)
+    {
+        if (mimeTypes != null)
+        {
+            _mimeTypes = new HashSet<String>();
+            StringTokenizer tok = new StringTokenizer(mimeTypes,",",false);
+            while (tok.hasMoreTokens())
+            {
+                _mimeTypes.add(tok.nextToken());
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Get the excluded user agents.
+     * 
+     * @return excluded user agents
+     */
+    public Set<String> getExcluded()
+    {
+        return _excluded;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set the excluded user agents.
+     * 
+     * @param excluded
+     *            excluded user agents to set
+     */
+    public void setExcluded(Set<String> excluded)
+    {
+        _excluded = excluded;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set the excluded user agents.
+     * 
+     * @param excluded
+     *            excluded user agents to set
+     */
+    public void setExcluded(String excluded)
+    {
+        if (excluded != null)
+        {
+            _excluded = new HashSet<String>();
+            StringTokenizer tok = new StringTokenizer(excluded,",",false);
+            while (tok.hasMoreTokens())
+                _excluded.add(tok.nextToken());
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The value of the Vary header set if a response can be compressed.
+     */
+    public String getVary()
+    {
+        return _vary;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set the value of the Vary header sent with responses that could be compressed.  
+     * <p>
+     * By default it is set to 'Accept-Encoding, User-Agent' since IE6 is excluded by 
+     * default from the excludedAgents. If user-agents are not to be excluded, then 
+     * this can be set to 'Accept-Encoding'.  Note also that shared caches may cache 
+     * many copies of a resource that is varied by User-Agent - one per variation of the 
+     * User-Agent, unless the cache does some normalization of the UA string.
+     * @param vary The value of the Vary header set if a response can be compressed.
+     */
+    public void setVary(String vary)
+    {
+        _vary = vary;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Get the buffer size.
+     * 
+     * @return the buffer size
+     */
+    public int getBufferSize()
+    {
+        return _bufferSize;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set the buffer size.
+     * 
+     * @param bufferSize
+     *            buffer size to set
+     */
+    public void setBufferSize(int bufferSize)
+    {
+        _bufferSize = bufferSize;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Get the minimum reponse size.
+     * 
+     * @return minimum reponse size
+     */
+    public int getMinGzipSize()
+    {
+        return _minGzipSize;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set the minimum reponse size.
+     * 
+     * @param minGzipSize
+     *            minimum reponse size
+     */
+    public void setMinGzipSize(int minGzipSize)
+    {
+        _minGzipSize = minGzipSize;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.handler.HandlerWrapper#handle(java.lang.String, org.eclipse.jetty.server.Request, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+     */
+    @Override
+    public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+    {
+        if (_handler!=null && isStarted())
+        {
+            String ae = request.getHeader("accept-encoding");
+            if (ae != null && ae.indexOf("gzip")>=0 && !response.containsHeader("Content-Encoding")
+                    && !HttpMethods.HEAD.equalsIgnoreCase(request.getMethod()))
+            {
+                if (_excluded!=null)
+                {
+                    String ua = request.getHeader("User-Agent");
+                    if (_excluded.contains(ua))
+                    {
+                        _handler.handle(target,baseRequest, request, response);
+                        return;
+                    }
+                }
+
+                final CompressedResponseWrapper wrappedResponse = newGzipResponseWrapper(request,response);
+                
+                boolean exceptional=true;
+                try
+                {
+                    _handler.handle(target, baseRequest, request, wrappedResponse);
+                    exceptional=false;
+                }
+                finally
+                {
+                    Continuation continuation = ContinuationSupport.getContinuation(request);
+                    if (continuation.isSuspended() && continuation.isResponseWrapped())   
+                    {
+                        continuation.addContinuationListener(new ContinuationListener()
+                        {
+                            public void onComplete(Continuation continuation)
+                            {
+                                try
+                                {
+                                    wrappedResponse.finish();
+                                }
+                                catch(IOException e)
+                                {
+                                    LOG.warn(e);
+                                }
+                            }
+
+                            public void onTimeout(Continuation continuation)
+                            {}
+                        });
+                    }
+                    else if (exceptional && !response.isCommitted())
+                    {
+                        wrappedResponse.resetBuffer();
+                        wrappedResponse.noCompression();
+                    }
+                    else
+                        wrappedResponse.finish();
+                }
+            }
+            else
+            {
+                _handler.handle(target,baseRequest, request, response);
+            }
+        }
+    }
+
+    /**
+     * Allows derived implementations to replace ResponseWrapper implementation.
+     *
+     * @param request the request
+     * @param response the response
+     * @return the gzip response wrapper
+     */
+    protected CompressedResponseWrapper newGzipResponseWrapper(HttpServletRequest request, HttpServletResponse response)
+    {
+        return new CompressedResponseWrapper(request,response)
+        {
+            {
+                super.setMimeTypes(GzipHandler.this._mimeTypes);
+                super.setBufferSize(GzipHandler.this._bufferSize);
+                super.setMinCompressSize(GzipHandler.this._minGzipSize);
+            }
+            
+            @Override
+            protected AbstractCompressedStream newCompressedStream(HttpServletRequest request,HttpServletResponse response) throws IOException
+            {
+                return new AbstractCompressedStream("gzip",request,this,_vary)
+                {
+                    @Override
+                    protected DeflaterOutputStream createStream() throws IOException
+                    {
+                        return new GZIPOutputStream(_response.getOutputStream(),_bufferSize);
+                    }
+                };
+            }
+            
+            @Override
+            protected PrintWriter newWriter(OutputStream out,String encoding) throws UnsupportedEncodingException
+            {
+                return GzipHandler.this.newWriter(out,encoding);
+            }
+        };
+    }
+    
+    /**
+     * Allows derived implementations to replace PrintWriter implementation.
+     *
+     * @param out the out
+     * @param encoding the encoding
+     * @return the prints the writer
+     * @throws UnsupportedEncodingException
+     */
+    protected PrintWriter newWriter(OutputStream out,String encoding) throws UnsupportedEncodingException
+    {
+        return encoding==null?new PrintWriter(out):new PrintWriter(new OutputStreamWriter(out,encoding));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/handler/HandlerCollection.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,316 @@
+//
+//  ========================================================================
+//  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.server.handler;
+
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.util.LazyList;
+import org.eclipse.jetty.util.MultiException;
+
+/* ------------------------------------------------------------ */
+/** A collection of handlers.  
+ * <p>
+ * The default implementations  calls all handlers in list order, 
+ * regardless of the response status or exceptions. Derived implementation
+ * may alter the order or the conditions of calling the contained 
+ * handlers.
+ * <p>
+ * 
+ * @org.apache.xbean.XBean
+ */
+public class HandlerCollection extends AbstractHandlerContainer
+{
+    private final boolean _mutableWhenRunning;
+    private volatile Handler[] _handlers;
+    private boolean _parallelStart=false; 
+
+    /* ------------------------------------------------------------ */
+    public HandlerCollection()
+    {
+        _mutableWhenRunning=false;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public HandlerCollection(boolean mutableWhenRunning)
+    {
+        _mutableWhenRunning=mutableWhenRunning;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the handlers.
+     */
+    public Handler[] getHandlers()
+    {
+        return _handlers;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * 
+     * @param handlers The handlers to set.
+     */
+    public void setHandlers(Handler[] handlers)
+    {
+        if (!_mutableWhenRunning && isStarted())
+            throw new IllegalStateException(STARTED);
+        
+        Handler [] old_handlers = _handlers==null?null:_handlers.clone();
+        _handlers = handlers;
+        
+        Server server = getServer();
+        MultiException mex = new MultiException();
+        for (int i=0;handlers!=null && i<handlers.length;i++)
+        {
+            if (handlers[i].getServer()!=server)
+                handlers[i].setServer(server);
+        }
+
+        if (getServer()!=null)
+            getServer().getContainer().update(this, old_handlers, handlers, "handler");
+        
+        // stop old handlers
+        for (int i=0;old_handlers!=null && i<old_handlers.length;i++)
+        {
+            if (old_handlers[i]!=null)
+            {
+                try
+                {
+                    if (old_handlers[i].isStarted())
+                        old_handlers[i].stop();
+                }
+                catch (Throwable e)
+                {
+                    mex.add(e);
+                }
+            }
+        }
+                
+        mex.ifExceptionThrowRuntime();
+    }
+    
+
+    
+    /* ------------------------------------------------------------ */
+    /** Get the parrallelStart.
+     * @return true if the contained handlers are started in parallel.
+     */
+    public boolean isParallelStart()
+    {
+        return _parallelStart;
+    }
+
+
+
+    /* ------------------------------------------------------------ */
+    /** Set the parallelStart.
+     * @param parallelStart If true, contained handlers are started in parallel.
+     */
+    public void setParallelStart(boolean parallelStart)
+    {
+        this._parallelStart = parallelStart;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see Handler#handle(String, Request, HttpServletRequest, HttpServletResponse)
+     */
+    public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) 
+        throws IOException, ServletException
+    {
+        if (_handlers!=null && isStarted())
+        {
+            MultiException mex=null;
+            
+            for (int i=0;i<_handlers.length;i++)
+            {
+                try
+                {
+                    _handlers[i].handle(target,baseRequest, request, response);
+                }
+                catch(IOException e)
+                {
+                    throw e;
+                }
+                catch(RuntimeException e)
+                {
+                    throw e;
+                }
+                catch(Exception e)
+                {
+                    if (mex==null)
+                        mex=new MultiException();
+                    mex.add(e);
+                }
+            }
+            if (mex!=null)
+            {
+                if (mex.size()==1)
+                    throw new ServletException(mex.getThrowable(0));
+                else
+                    throw new ServletException(mex);
+            }
+            
+        }    
+    }
+
+    /* ------------------------------------------------------------ */
+    /* 
+     * @see org.eclipse.jetty.server.server.handler.AbstractHandler#doStart()
+     */
+    @Override
+    protected void doStart() throws Exception
+    {
+        final MultiException mex=new MultiException();
+        if (_handlers!=null)
+        {
+            if (_parallelStart)
+            {
+                final CountDownLatch latch = new CountDownLatch(_handlers.length);
+                final ClassLoader loader = Thread.currentThread().getContextClassLoader();
+                for (int i=0;i<_handlers.length;i++)
+                {
+                    final int h=i;
+                    getServer().getThreadPool().dispatch(
+                            new Runnable()
+                            {
+                                public void run()
+                                {
+                                    ClassLoader orig = Thread.currentThread().getContextClassLoader();
+                                    try
+                                    {
+                                        Thread.currentThread().setContextClassLoader(loader);
+                                        _handlers[h].start();
+                                    }
+                                    catch(Throwable e)
+                                    {
+                                        mex.add(e);
+                                    }
+                                    finally
+                                    {
+                                        Thread.currentThread().setContextClassLoader(orig);
+                                        latch.countDown();
+                                    }
+                                }
+                            }
+                    );
+                }
+                latch.await();
+            }
+            else
+            {
+                for (int i=0;i<_handlers.length;i++)
+                {
+                    try{_handlers[i].start();}
+                    catch(Throwable e){mex.add(e);}
+                }
+            }
+        }
+        super.doStart();
+        mex.ifExceptionThrow();
+    }
+
+    /* ------------------------------------------------------------ */
+    /* 
+     * @see org.eclipse.jetty.server.server.handler.AbstractHandler#doStop()
+     */
+    @Override
+    protected void doStop() throws Exception
+    {
+        MultiException mex=new MultiException();
+        try { super.doStop(); } catch(Throwable e){mex.add(e);}
+        if (_handlers!=null)
+        {
+            for (int i=_handlers.length;i-->0;)
+                try{_handlers[i].stop();}catch(Throwable e){mex.add(e);}
+        }
+        mex.ifExceptionThrow();
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public void setServer(Server server)
+    {
+        if (isStarted())
+            throw new IllegalStateException(STARTED);
+        
+        Server old_server=getServer();
+        
+        super.setServer(server);
+
+        Handler[] h=getHandlers();
+        for (int i=0;h!=null && i<h.length;i++)
+            h[i].setServer(server);
+        
+        if (server!=null && server!=old_server)
+            server.getContainer().update(this, null,_handlers, "handler");
+        
+    }
+
+    /* ------------------------------------------------------------ */
+    /* Add a handler.
+     * This implementation adds the passed handler to the end of the existing collection of handlers. 
+     * @see org.eclipse.jetty.server.server.HandlerContainer#addHandler(org.eclipse.jetty.server.server.Handler)
+     */
+    public void addHandler(Handler handler)
+    {
+        setHandlers((Handler[])LazyList.addToArray(getHandlers(), handler, Handler.class));
+    }
+    
+    /* ------------------------------------------------------------ */
+    public void removeHandler(Handler handler)
+    {
+        Handler[] handlers = getHandlers();
+        
+        if (handlers!=null && handlers.length>0 )
+            setHandlers((Handler[])LazyList.removeFromArray(handlers, handler));
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    protected Object expandChildren(Object list, Class byClass)
+    {
+        Handler[] handlers = getHandlers();
+        for (int i=0;handlers!=null && i<handlers.length;i++)
+            list=expandHandler(handlers[i], list, byClass);
+        return list;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void destroy()
+    {
+        if (!isStopped())
+            throw new IllegalStateException("!STOPPED");
+        Handler[] children=getChildHandlers();
+        setHandlers(null);
+        for (Handler child: children)
+            child.destroy();
+        super.destroy();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/handler/HandlerList.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,58 @@
+//
+//  ========================================================================
+//  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.server.handler;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.Request;
+
+/* ------------------------------------------------------------ */
+/** HandlerList.
+ * This extension of {@link HandlerCollection} will call
+ * each contained handler in turn until either an exception is thrown, the response 
+ * is committed or a positive response status is set.
+ */
+public class HandlerList extends HandlerCollection
+{
+    /* ------------------------------------------------------------ */
+    /**
+     * @see Handler#handle(String, Request, HttpServletRequest, HttpServletResponse)
+     */
+    @Override
+    public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) 
+        throws IOException, ServletException
+    {
+        Handler[] handlers = getHandlers();
+        
+        if (handlers!=null && isStarted())
+        {
+            for (int i=0;i<handlers.length;i++)
+            {
+                handlers[i].handle(target,baseRequest, request, response);
+                if ( baseRequest.isHandled())
+                    return;
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/handler/HandlerWrapper.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,182 @@
+//
+//  ========================================================================
+//  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.server.handler;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.util.component.LifeCycle;
+
+/* ------------------------------------------------------------ */
+/** A <code>HandlerWrapper</code> acts as a {@link Handler} but delegates the {@link Handler#handle handle} method and
+ * {@link LifeCycle life cycle} events to a delegate. This is primarily used to implement the <i>Decorator</i> pattern.
+ *
+ */
+public class HandlerWrapper extends AbstractHandlerContainer
+{
+    protected Handler _handler;
+
+    /* ------------------------------------------------------------ */
+    /**
+     *
+     */
+    public HandlerWrapper()
+    {
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the handlers.
+     */
+    public Handler getHandler()
+    {
+        return _handler;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the handlers.
+     */
+    public Handler[] getHandlers()
+    {
+        if (_handler==null)
+            return new Handler[0];
+        return new Handler[] {_handler};
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param handler Set the {@link Handler} which should be wrapped.
+     */
+    public void setHandler(Handler handler)
+    {
+        if (isStarted())
+            throw new IllegalStateException(STARTED);
+
+        Handler old_handler = _handler;
+        _handler = handler;
+        if (handler!=null)
+            handler.setServer(getServer());
+        
+        if (getServer()!=null)
+            getServer().getContainer().update(this, old_handler, handler, "handler");
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.thread.AbstractLifeCycle#doStart()
+     */
+    @Override
+    protected void doStart() throws Exception
+    {
+        if (_handler!=null)
+            _handler.start();
+        super.doStart();
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.thread.AbstractLifeCycle#doStop()
+     */
+    @Override
+    protected void doStop() throws Exception
+    {
+        if (_handler!=null)
+            _handler.stop();
+        super.doStop();
+    }
+
+    /* ------------------------------------------------------------ */
+    public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+    {
+        if (_handler!=null && isStarted())
+        {
+            _handler.handle(target,baseRequest, request, response);
+        }
+    }
+
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void setServer(Server server)
+    {
+        Server old_server=getServer();
+        if (server==old_server)
+            return;
+
+        if (isStarted())
+            throw new IllegalStateException(STARTED);
+
+        super.setServer(server);
+
+        Handler h=getHandler();
+        if (h!=null)
+            h.setServer(server);
+
+        if (server!=null && server!=old_server)
+            server.getContainer().update(this, null,_handler, "handler");
+    }
+
+
+    /* ------------------------------------------------------------ */
+    @Override
+    protected Object expandChildren(Object list, Class byClass)
+    {
+        return expandHandler(_handler,list,byClass);
+    }
+
+    /* ------------------------------------------------------------ */
+    public <H extends Handler> H getNestedHandlerByClass(Class<H> byclass)
+    {
+        HandlerWrapper h=this;
+        while (h!=null)
+        {
+            if (byclass.isInstance(h))
+                return (H)h;
+            Handler w = h.getHandler();
+            if (w instanceof HandlerWrapper)
+                h=(HandlerWrapper)w;
+            else break;
+        }
+        return null;
+
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void destroy()
+    {
+        if (!isStopped())
+            throw new IllegalStateException("!STOPPED");
+        Handler child=getHandler();
+        if (child!=null)
+        {
+            setHandler(null);
+            child.destroy();
+        }
+        super.destroy();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/handler/HotSwapHandler.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,176 @@
+//
+//  ========================================================================
+//  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.server.handler;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+
+/* ------------------------------------------------------------ */
+/**
+ * A <code>HandlerContainer</code> that allows a hot swap of a wrapped handler.
+ * 
+ */
+public class HotSwapHandler extends AbstractHandlerContainer
+{
+    private volatile Handler _handler;
+
+    /* ------------------------------------------------------------ */
+    /**
+     * 
+     */
+    public HotSwapHandler()
+    {
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the handlers.
+     */
+    public Handler getHandler()
+    {
+        return _handler;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the handlers.
+     */
+    public Handler[] getHandlers()
+    {
+        return new Handler[]
+        { _handler };
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param handler
+     *            Set the {@link Handler} which should be wrapped.
+     */
+    public void setHandler(Handler handler)
+    {
+        if (handler == null)
+            throw new IllegalArgumentException("Parameter handler is null.");
+        try
+        {
+            Handler old_handler = _handler;
+            _handler = handler;
+            Server server = getServer();
+            handler.setServer(server);
+            addBean(handler);
+
+            if (server != null)
+                server.getContainer().update(this,old_handler,handler,"handler");
+
+            // if there is an old handler and it was started, stop it
+            if (old_handler != null)
+            {
+                removeBean(old_handler);
+            }
+
+        }
+        catch (Exception e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.thread.AbstractLifeCycle#doStart()
+     */
+    @Override
+    protected void doStart() throws Exception
+    {
+        super.doStart();
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.thread.AbstractLifeCycle#doStop()
+     */
+    @Override
+    protected void doStop() throws Exception
+    {
+        super.doStop();
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.jetty.server.server.EventHandler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+     */
+    public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+    {
+        if (_handler != null && isStarted())
+        {
+            _handler.handle(target,baseRequest,request,response);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void setServer(Server server)
+    {
+        Server old_server = getServer();
+        if (server == old_server)
+            return;
+
+        if (isRunning())
+            throw new IllegalStateException(RUNNING);
+
+        super.setServer(server);
+
+        Handler h = getHandler();
+        if (h != null)
+            h.setServer(server);
+
+        if (server != null && server != old_server)
+            server.getContainer().update(this,null,_handler,"handler");
+    }
+
+    /* ------------------------------------------------------------ */
+    @SuppressWarnings(
+    { "rawtypes", "unchecked" })
+    @Override
+    protected Object expandChildren(Object list, Class byClass)
+    {
+        return expandHandler(_handler,list,byClass);
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void destroy()
+    {
+        if (!isStopped())
+            throw new IllegalStateException("!STOPPED");
+        Handler child = getHandler();
+        if (child != null)
+        {
+            setHandler(null);
+            child.destroy();
+        }
+        super.destroy();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/handler/IPAccessHandler.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,382 @@
+//
+//  ========================================================================
+//  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.server.handler;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.PathMap;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.server.AbstractHttpConnection;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.util.IPAddressMap;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+
+/**
+ * IP Access Handler
+ * <p>
+ * Controls access to the wrapped handler by the real remote IP. Control is provided
+ * by white/black lists that include both internet addresses and URIs. This handler
+ * uses the real internet address of the connection, not one reported in the forwarded
+ * for headers, as this cannot be as easily forged. 
+ * <p>
+ * Typically, the black/white lists will be used in one of three modes:
+ * <ul>
+ * <li>Blocking a few specific IPs/URLs by specifying several black list entries.
+ * <li>Allowing only some specific IPs/URLs by specifying several white lists entries.
+ * <li>Allowing a general range of IPs/URLs by specifying several general white list
+ * entries, that are then further refined by several specific black list exceptions
+ * </ul>
+ * <p>
+ * An empty white list is treated as match all. If there is at least one entry in
+ * the white list, then a request must match a white list entry. Black list entries
+ * are always applied, so that even if an entry matches the white list, a black list 
+ * entry will override it.
+ * <p>
+ * Internet addresses may be specified as absolute address or as a combination of 
+ * four octet wildcard specifications (a.b.c.d) that are defined as follows.
+ * </p>
+ * <pre>
+ * nnn - an absolute value (0-255)
+ * mmm-nnn - an inclusive range of absolute values, 
+ *           with following shorthand notations:
+ *           nnn- => nnn-255
+ *           -nnn => 0-nnn
+ *           -    => 0-255
+ * a,b,... - a list of wildcard specifications
+ * </pre>
+ * <p>
+ * Internet address specification is separated from the URI pattern using the "|" (pipe)
+ * character. URI patterns follow the servlet specification for simple * prefix and 
+ * suffix wild cards (e.g. /, /foo, /foo/bar, /foo/bar/*, *.baz).
+ * <p>
+ * Earlier versions of the handler used internet address prefix wildcard specification
+ * to define a range of the internet addresses (e.g. 127., 10.10., 172.16.1.).
+ * They also used the first "/" character of the URI pattern to separate it from the 
+ * internet address. Both of these features have been deprecated in the current version. 
+ * <p>
+ * Examples of the entry specifications are:
+ * <ul>
+ * <li>10.10.1.2 - all requests from IP 10.10.1.2
+ * <li>10.10.1.2|/foo/bar - all requests from IP 10.10.1.2 to URI /foo/bar
+ * <li>10.10.1.2|/foo/* - all requests from IP 10.10.1.2 to URIs starting with /foo/
+ * <li>10.10.1.2|*.html - all requests from IP 10.10.1.2 to URIs ending with .html
+ * <li>10.10.0-255.0-255 - all requests from IPs within 10.10.0.0/16 subnet
+ * <li>10.10.0-.-255|/foo/bar - all requests from IPs within 10.10.0.0/16 subnet to URI /foo/bar
+ * <li>10.10.0-3,1,3,7,15|/foo/* - all requests from IPs addresses with last octet equal
+ *                                  to 1,3,7,15 in subnet 10.10.0.0/22 to URIs starting with /foo/
+ * </ul>
+ * <p>
+ * Earlier versions of the handler used internet address prefix wildcard specification
+ * to define a range of the internet addresses (e.g. 127., 10.10., 172.16.1.).
+ * They also used the first "/" character of the URI pattern to separate it from the 
+ * internet address. Both of these features have been deprecated in the current version. 
+ */
+public class IPAccessHandler extends HandlerWrapper
+{
+    private static final Logger LOG = Log.getLogger(IPAccessHandler.class);
+
+    IPAddressMap<PathMap> _white = new IPAddressMap<PathMap>();
+    IPAddressMap<PathMap> _black = new IPAddressMap<PathMap>();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Creates new handler object
+     */
+    public IPAccessHandler()
+    {
+        super();
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Creates new handler object and initializes white- and black-list
+     * 
+     * @param white array of whitelist entries
+     * @param black array of blacklist entries
+     */
+    public IPAccessHandler(String[] white, String []black)
+    {
+        super();
+        
+        if (white != null && white.length > 0)
+            setWhite(white);
+        if (black != null && black.length > 0)
+            setBlack(black);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Add a whitelist entry to an existing handler configuration
+     * 
+     * @param entry new whitelist entry
+     */
+    public void addWhite(String entry)
+    {
+        add(entry, _white);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Add a blacklist entry to an existing handler configuration
+     * 
+     * @param entry new blacklist entry
+     */
+    public void addBlack(String entry)
+    {
+        add(entry, _black);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Re-initialize the whitelist of existing handler object
+     * 
+     * @param entries array of whitelist entries
+     */
+    public void setWhite(String[] entries)
+    {
+        set(entries, _white);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Re-initialize the blacklist of existing handler object
+     * 
+     * @param entries array of blacklist entries
+     */
+    public void setBlack(String[] entries)
+    {
+        set(entries, _black);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Checks the incoming request against the whitelist and blacklist
+     * 
+     * @see org.eclipse.jetty.server.handler.HandlerWrapper#handle(java.lang.String, org.eclipse.jetty.server.Request, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+     */
+    @Override
+    public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+    {
+        // Get the real remote IP (not the one set by the forwarded headers (which may be forged))
+        AbstractHttpConnection connection = baseRequest.getConnection();
+        if (connection!=null)
+        {
+            EndPoint endp=connection.getEndPoint();
+            if (endp!=null)
+            {
+                String addr = endp.getRemoteAddr();
+                if (addr!=null && !isAddrUriAllowed(addr,baseRequest.getPathInfo()))
+                {
+                    response.sendError(HttpStatus.FORBIDDEN_403);
+                    baseRequest.setHandled(true);
+                    return;
+                }
+            }
+        }
+        
+        getHandler().handle(target,baseRequest, request, response);
+    }
+    
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Helper method to parse the new entry and add it to 
+     * the specified address pattern map.
+     * 
+     * @param entry new entry
+     * @param patternMap target address pattern map
+     */
+    protected void add(String entry, IPAddressMap<PathMap> patternMap)
+    {
+        if (entry != null && entry.length() > 0)
+        {
+            boolean deprecated = false;
+            int idx;
+            if (entry.indexOf('|') > 0 )
+            {
+                idx = entry.indexOf('|');
+            }
+            else
+            {
+                idx = entry.indexOf('/');
+                deprecated = (idx >= 0);
+            }
+            
+            String addr = idx > 0 ? entry.substring(0,idx) : entry;        
+            String path = idx > 0 ? entry.substring(idx) : "/*";
+            
+            if (addr.endsWith("."))
+                deprecated = true;
+            if (path!=null && (path.startsWith("|") || path.startsWith("/*.")))
+                path=path.substring(1);
+           
+            PathMap pathMap = patternMap.get(addr);
+            if (pathMap == null)
+            {
+                pathMap = new PathMap(true);
+                patternMap.put(addr,pathMap);
+            }
+            if (path != null && !"".equals(path))
+                pathMap.put(path,path);
+            
+            if (deprecated)
+                LOG.debug(toString() +" - deprecated specification syntax: "+entry);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Helper method to process a list of new entries and replace 
+     * the content of the specified address pattern map
+     * 
+     * @param entries new entries
+     * @param patternMap target address pattern map
+     */
+    protected void set(String[] entries,  IPAddressMap<PathMap> patternMap)
+    {
+        patternMap.clear();
+        
+        if (entries != null && entries.length > 0)
+        {
+            for (String addrPath:entries)
+            {
+                add(addrPath, patternMap);
+            }
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Check if specified request is allowed by current IPAccess rules.
+     * 
+     * @param addr internet address
+     * @param path context path
+     * @return true if request is allowed
+     *
+     */
+    protected boolean isAddrUriAllowed(String addr, String path)
+    {
+        if (_white.size()>0)
+        {
+            boolean match = false;
+            
+            Object whiteObj = _white.getLazyMatches(addr);
+            if (whiteObj != null) 
+            {
+                List whiteList = (whiteObj instanceof List) ? (List)whiteObj : Collections.singletonList(whiteObj);
+
+                for (Object entry: whiteList)
+                {
+                    PathMap pathMap = ((Map.Entry<String,PathMap>)entry).getValue();
+                    if (match = (pathMap!=null && (pathMap.size()==0 || pathMap.match(path)!=null)))
+                        break;
+                }
+            }
+            
+            if (!match)
+                return false;
+        }
+
+        if (_black.size() > 0)
+        {
+            Object blackObj = _black.getLazyMatches(addr);
+            if (blackObj != null) 
+            {
+                List blackList = (blackObj instanceof List) ? (List)blackObj : Collections.singletonList(blackObj);
+    
+                for (Object entry: blackList)
+                {
+                    PathMap pathMap = ((Map.Entry<String,PathMap>)entry).getValue();
+                    if (pathMap!=null && (pathMap.size()==0 || pathMap.match(path)!=null))
+                        return false;
+                }
+            }
+        }
+        
+        return true;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Dump the white- and black-list configurations when started
+     * 
+     * @see org.eclipse.jetty.server.handler.HandlerWrapper#doStart()
+     */
+    @Override
+    protected void doStart()
+        throws Exception
+    {
+        super.doStart();
+        
+        if (LOG.isDebugEnabled())
+        {
+            System.err.println(dump());
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Dump the handler configuration
+     */
+    public String dump()
+    {
+        StringBuilder buf = new StringBuilder();
+        
+        buf.append(toString());
+        buf.append(" WHITELIST:\n");
+        dump(buf, _white);
+        buf.append(toString());
+        buf.append(" BLACKLIST:\n");
+        dump(buf, _black);
+        
+        return buf.toString();
+    }    
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Dump a pattern map into a StringBuilder buffer
+     * 
+     * @param buf buffer
+     * @param patternMap pattern map to dump
+     */
+    protected void dump(StringBuilder buf, IPAddressMap<PathMap> patternMap)
+    {
+        for (String addr: patternMap.keySet())
+        {
+            for (Object path: ((PathMap)patternMap.get(addr)).values())
+            {
+                buf.append("# ");
+                buf.append(addr);
+                buf.append("|");
+                buf.append(path);
+                buf.append("\n");
+            }       
+        }
+    }
+ }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/handler/IdleTimeoutHandler.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,138 @@
+//
+//  ========================================================================
+//  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.server.handler;
+
+import java.io.IOException;
+
+import javax.servlet.AsyncEvent;
+import javax.servlet.AsyncListener;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.server.AbstractHttpConnection;
+import org.eclipse.jetty.server.Request;
+
+/**
+ * Handler to adjust the idle timeout of requests while dispatched.
+ * 
+ * <p>Can be applied in jetty.xml with
+ * <pre>
+ *   &lt;Get id='handler' name='Handler'/>
+ *   &lt;Set name='Handler'>
+ *     &lt;New id='idleTimeoutHandler' class='org.eclipse.jetty.server.handler.IdleTimeoutHandler'>
+ *       &lt;Set name='Handler'>&lt;Ref id='handler'/>&lt;/Set>
+ *       &lt;Set name='IdleTimeoutMs'>5000&lt;/Set>
+ *     &lt;/New>
+ *   &lt;/Set>
+ * </pre>
+ */
+public class IdleTimeoutHandler extends HandlerWrapper
+{
+    private int _idleTimeoutMs = 1000;
+    private boolean _applyToAsync = false;
+    
+    
+    public boolean isApplyToAsync()
+    {
+        return _applyToAsync;
+    }
+
+    /**
+     * Should the adjusted idle time be maintained for asynchronous requests
+     * @param applyToAsync true if alternate idle timeout is applied to asynchronous requests
+     */
+    public void setApplyToAsync(boolean applyToAsync)
+    {
+        _applyToAsync = applyToAsync;
+    }
+
+    public long getIdleTimeoutMs()
+    {
+        return _idleTimeoutMs;
+    }
+
+    /**
+     * @param idleTimeoutMs The idle timeout in MS to apply while dispatched or async
+     */
+    public void setIdleTimeoutMs(int _idleTimeoutMs)
+    {
+        this._idleTimeoutMs = _idleTimeoutMs;
+    }
+    
+   
+    @Override
+    public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+    {
+        AbstractHttpConnection connection = AbstractHttpConnection.getCurrentConnection();
+        final EndPoint endp = connection==null?null:connection.getEndPoint();
+        
+        final int idle_timeout;
+        if (endp==null)
+            idle_timeout=-1;
+        else
+        {
+            idle_timeout=endp.getMaxIdleTime();
+            endp.setMaxIdleTime(_idleTimeoutMs);
+        }
+        
+        try
+        {
+            super.handle(target,baseRequest,request,response);
+        }
+        finally
+        {
+            if (endp!=null)
+            {
+                if (_applyToAsync && request.isAsyncStarted())
+                {
+                    request.getAsyncContext().addListener(new AsyncListener()
+                    {
+                        @Override
+                        public void onTimeout(AsyncEvent event) throws IOException
+                        {                            
+                        }
+                        
+                        @Override
+                        public void onStartAsync(AsyncEvent event) throws IOException
+                        {
+                        }
+                        
+                        @Override
+                        public void onError(AsyncEvent event) throws IOException
+                        {
+                            endp.setMaxIdleTime(idle_timeout);
+                        }
+                        
+                        @Override
+                        public void onComplete(AsyncEvent event) throws IOException
+                        {
+                            endp.setMaxIdleTime(idle_timeout);
+                        }
+                    });
+                }
+                else 
+                {
+                    endp.setMaxIdleTime(idle_timeout);
+                }
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/handler/MovedContextHandler.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,154 @@
+//
+//  ========================================================================
+//  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.server.handler;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.HttpHeaders;
+import org.eclipse.jetty.server.HandlerContainer;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.util.URIUtil;
+
+/* ------------------------------------------------------------ */
+/** Moved ContextHandler.
+ * This context can be used to replace a context that has changed
+ * location.  Requests are redirected (either to a fixed URL or to a
+ * new context base). 
+ */
+public class MovedContextHandler extends ContextHandler
+{
+    final Redirector _redirector;
+    String _newContextURL;
+    boolean _discardPathInfo;
+    boolean _discardQuery;
+    boolean _permanent;
+    String _expires;
+
+    public MovedContextHandler()
+    {
+        _redirector=new Redirector();
+        setHandler(_redirector);
+        setAllowNullPathInfo(true);
+    }
+    
+    public MovedContextHandler(HandlerContainer parent, String contextPath, String newContextURL)
+    {
+        super(parent,contextPath);
+        _newContextURL=newContextURL;
+        _redirector=new Redirector();
+        setHandler(_redirector);
+    }
+
+    public boolean isDiscardPathInfo()
+    {
+        return _discardPathInfo;
+    }
+
+    public void setDiscardPathInfo(boolean discardPathInfo)
+    {
+        _discardPathInfo = discardPathInfo;
+    }
+
+    public String getNewContextURL()
+    {
+        return _newContextURL;
+    }
+
+    public void setNewContextURL(String newContextURL)
+    {
+        _newContextURL = newContextURL;
+    }
+
+    public boolean isPermanent()
+    {
+        return _permanent;
+    }
+
+    public void setPermanent(boolean permanent)
+    {
+        _permanent = permanent;
+    }
+
+    public boolean isDiscardQuery()
+    {
+        return _discardQuery;
+    }
+
+    public void setDiscardQuery(boolean discardQuery)
+    {
+        _discardQuery = discardQuery;
+    }
+    
+    private class Redirector extends AbstractHandler
+    {
+        public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+        {
+            if (_newContextURL==null)
+                return;
+
+            String path=_newContextURL;
+            if (!_discardPathInfo && request.getPathInfo()!=null)
+                path=URIUtil.addPaths(path, request.getPathInfo());
+            
+            StringBuilder location = URIUtil.hasScheme(path)?new StringBuilder():baseRequest.getRootURL();
+
+            location.append(path);
+            if (!_discardQuery && request.getQueryString()!=null)
+            {
+                location.append('?');
+                String q=request.getQueryString();
+                q=q.replaceAll("\r\n?&=","!");
+                location.append(q);
+            }
+            
+            response.setHeader(HttpHeaders.LOCATION,location.toString());
+
+            if (_expires!=null)
+                response.setHeader(HttpHeaders.EXPIRES,_expires);
+            
+            response.setStatus(_permanent?HttpServletResponse.SC_MOVED_PERMANENTLY:HttpServletResponse.SC_FOUND);
+            response.setContentLength(0);
+            baseRequest.setHandled(true);
+        }
+        
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return the expires header value or null if no expires header
+     */
+    public String getExpires()
+    {
+        return _expires;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param expires the expires header value or null if no expires header
+     */
+    public void setExpires(String expires)
+    {
+        _expires = expires;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/handler/ProxyHandler.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,51 @@
+//
+//  ========================================================================
+//  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.server.handler;
+
+import org.eclipse.jetty.server.Handler;
+
+
+/* ------------------------------------------------------------ */
+/** ProxyHandler.
+ * <p>This class has been renamed to ConnectHandler, as it only implements
+ * the CONNECT method (and a ProxyServlet must be used for full proxy handling).
+ * @deprecated Use {@link ConnectHandler}
+ */
+public class ProxyHandler extends ConnectHandler
+{
+    public ProxyHandler()
+    {
+        super();
+    }
+
+    public ProxyHandler(Handler handler, String[] white, String[] black)
+    {
+        super(handler,white,black);
+    }
+
+    public ProxyHandler(Handler handler)
+    {
+        super(handler);
+    }
+
+    public ProxyHandler(String[] white, String[] black)
+    {
+        super(white,black);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/handler/RequestLogHandler.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,192 @@
+//
+//  ========================================================================
+//  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.server.handler; 
+
+import java.io.IOException;
+
+import javax.servlet.DispatcherType;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.continuation.Continuation;
+import org.eclipse.jetty.continuation.ContinuationListener;
+import org.eclipse.jetty.server.AsyncContinuation;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.RequestLog;
+import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.util.component.AbstractLifeCycle;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+
+/** 
+ * RequestLogHandler.
+ * This handler can be used to wrap an individual context for context logging.
+ * 
+ * @org.apache.xbean.XBean
+ */
+public class RequestLogHandler extends HandlerWrapper
+{
+    private static final Logger LOG = Log.getLogger(RequestLogHandler.class);
+
+    private RequestLog _requestLog;
+    
+    /* ------------------------------------------------------------ */
+    /* 
+     * @see org.eclipse.jetty.server.server.Handler#handle(java.lang.String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int)
+     */
+    @Override
+    public void handle(String target, final Request baseRequest, HttpServletRequest request, final HttpServletResponse response)
+            throws IOException, ServletException
+    {
+        AsyncContinuation continuation = baseRequest.getAsyncContinuation();
+        if (!continuation.isInitial())
+        {
+            baseRequest.setDispatchTime(System.currentTimeMillis());
+        }
+        
+        try
+        {
+            super.handle(target, baseRequest, request, response);
+        }
+        finally
+        {
+            if (_requestLog != null && baseRequest.getDispatcherType().equals(DispatcherType.REQUEST))
+            {
+                if (continuation.isAsync())
+                {
+                    if (continuation.isInitial())
+                        continuation.addContinuationListener(new ContinuationListener()
+                        {
+
+                            public void onTimeout(Continuation continuation)
+                            {
+
+                            }
+
+                            public void onComplete(Continuation continuation)
+                            {
+                                _requestLog.log(baseRequest, (Response)response);
+                            }
+                        });
+                }
+                else
+                    _requestLog.log(baseRequest, (Response)response);
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setRequestLog(RequestLog requestLog)
+    {
+        //are we changing the request log impl?
+        try
+        {
+            if (_requestLog != null)
+                _requestLog.stop();
+        }
+        catch (Exception e)
+        {
+            LOG.warn (e);
+        }
+        
+        if (getServer()!=null)
+            getServer().getContainer().update(this, _requestLog, requestLog, "logimpl",true);
+        
+        _requestLog = requestLog;
+        
+        //if we're already started, then start our request log
+        try
+        {
+            if (isStarted() && (_requestLog != null))
+                _requestLog.start();
+        }
+        catch (Exception e)
+        {
+            throw new RuntimeException (e);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /* 
+     * @see org.eclipse.jetty.server.server.handler.HandlerWrapper#setServer(org.eclipse.jetty.server.server.Server)
+     */
+    @Override
+    public void setServer(Server server)
+    {
+        if (_requestLog!=null)
+        {
+            if (getServer()!=null && getServer()!=server)
+                getServer().getContainer().update(this, _requestLog, null, "logimpl",true);
+            super.setServer(server);
+            if (server!=null && server!=getServer())
+                server.getContainer().update(this, null,_requestLog, "logimpl",true);
+        }
+        else
+            super.setServer(server);
+    }
+
+    /* ------------------------------------------------------------ */
+    public RequestLog getRequestLog() 
+    {
+        return _requestLog;
+    }
+
+    /* ------------------------------------------------------------ */
+    /* 
+     * @see org.eclipse.jetty.server.server.handler.HandlerWrapper#doStart()
+     */
+    @Override
+    protected void doStart() throws Exception
+    {
+        if (_requestLog==null)
+        {
+            LOG.warn("!RequestLog");
+            _requestLog=new NullRequestLog();
+        }
+        super.doStart();
+        _requestLog.start();
+    }
+
+    /* ------------------------------------------------------------ */
+    /* 
+     * @see org.eclipse.jetty.server.server.handler.HandlerWrapper#doStop()
+     */
+    @Override
+    protected void doStop() throws Exception
+    {
+        super.doStop();
+        _requestLog.stop();
+        if (_requestLog instanceof NullRequestLog)
+            _requestLog=null;
+    }
+
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    private static class NullRequestLog extends AbstractLifeCycle implements RequestLog
+    {
+        public void log(Request request, Response response)
+        {            
+        }
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/handler/ResourceHandler.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,545 @@
+//
+//  ========================================================================
+//  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.server.handler;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.MalformedURLException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpHeaders;
+import org.eclipse.jetty.http.HttpMethods;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.http.MimeTypes;
+import org.eclipse.jetty.io.Buffer;
+import org.eclipse.jetty.io.ByteArrayBuffer;
+import org.eclipse.jetty.io.WriterOutputStream;
+import org.eclipse.jetty.server.AbstractHttpConnection;
+import org.eclipse.jetty.server.Dispatcher;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.server.handler.ContextHandler.Context;
+import org.eclipse.jetty.util.URIUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.resource.FileResource;
+import org.eclipse.jetty.util.resource.Resource;
+
+
+/* ------------------------------------------------------------ */
+/** Resource Handler.
+ *
+ * This handle will serve static content and handle If-Modified-Since headers.
+ * No caching is done.
+ * Requests for resources that do not exist are let pass (Eg no 404's).
+ *
+ *
+ * @org.apache.xbean.XBean
+ */
+public class ResourceHandler extends HandlerWrapper
+{
+    private static final Logger LOG = Log.getLogger(ResourceHandler.class);
+
+    ContextHandler _context;
+    Resource _baseResource;
+    Resource _defaultStylesheet;
+    Resource _stylesheet;
+    String[] _welcomeFiles={"index.html"};
+    MimeTypes _mimeTypes = new MimeTypes();
+    ByteArrayBuffer _cacheControl;
+    boolean _aliases;
+    boolean _directory;
+    boolean _etags;
+
+    /* ------------------------------------------------------------ */
+    public ResourceHandler()
+    {
+    	
+    }
+
+    /* ------------------------------------------------------------ */
+    public MimeTypes getMimeTypes()
+    {
+        return _mimeTypes;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setMimeTypes(MimeTypes mimeTypes)
+    {
+        _mimeTypes = mimeTypes;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return True if resource aliases are allowed.
+     */
+    public boolean isAliases()
+    {
+        return _aliases;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set if resource aliases (eg symlink, 8.3 names, case insensitivity) are allowed.
+     * Allowing aliases can significantly increase security vulnerabilities.
+     * If this handler is deployed inside a ContextHandler, then the
+     * {@link ContextHandler#isAliases()} takes precedent.
+     * @param aliases True if aliases are supported.
+     */
+    public void setAliases(boolean aliases)
+    {
+        _aliases = aliases;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Get the directory option.
+     * @return true if directories are listed.
+     */
+    public boolean isDirectoriesListed()
+    {
+        return _directory;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set the directory.
+     * @param directory true if directories are listed.
+     */
+    public void setDirectoriesListed(boolean directory)
+    {
+        _directory = directory;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return True if ETag processing is done
+     */
+    public boolean isEtags()
+    {
+        return _etags;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param etags True if ETag processing is done
+     */
+    public void setEtags(boolean etags)
+    {
+        _etags = etags;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void doStart()
+    throws Exception
+    {
+        Context scontext = ContextHandler.getCurrentContext();
+        _context = (scontext==null?null:scontext.getContextHandler());
+
+        if (_context!=null)
+            _aliases=_context.isAliases();
+
+        if (!_aliases && !FileResource.getCheckAliases())
+            throw new IllegalStateException("Alias checking disabled");
+
+        super.doStart();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the resourceBase.
+     */
+    public Resource getBaseResource()
+    {
+        if (_baseResource==null)
+            return null;
+        return _baseResource;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the base resource as a string.
+     */
+    public String getResourceBase()
+    {
+        if (_baseResource==null)
+            return null;
+        return _baseResource.toString();
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param base The resourceBase to set.
+     */
+    public void setBaseResource(Resource base)
+    {
+        _baseResource=base;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param resourceBase The base resource as a string.
+     */
+    public void setResourceBase(String resourceBase)
+    {
+        try
+        {
+            setBaseResource(Resource.newResource(resourceBase));
+        }
+        catch (Exception e)
+        {
+            LOG.warn(e.toString());
+            LOG.debug(e);
+            throw new IllegalArgumentException(resourceBase);
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the stylesheet as a Resource.
+     */
+    public Resource getStylesheet()
+    {
+    	if(_stylesheet != null)
+    	{
+    	    return _stylesheet;
+    	}
+    	else
+    	{
+    	    if(_defaultStylesheet == null)
+    	    {
+    	        try
+    	        {
+    	            _defaultStylesheet =  Resource.newResource(this.getClass().getResource("/jetty-dir.css"));
+    	        }
+    	        catch(IOException e)
+    	        {
+    	            LOG.warn(e.toString());
+    	            LOG.debug(e);
+    	        }	 
+    	    }
+    	    return _defaultStylesheet;
+    	}
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @param stylesheet The location of the stylesheet to be used as a String.
+     */
+    public void setStylesheet(String stylesheet)
+    {
+        try
+        {
+            _stylesheet = Resource.newResource(stylesheet);
+            if(!_stylesheet.exists())
+            {
+                LOG.warn("unable to find custom stylesheet: " + stylesheet);
+                _stylesheet = null;
+            }
+        }
+    	catch(Exception e)
+    	{
+    		LOG.warn(e.toString());
+            LOG.debug(e);
+            throw new IllegalArgumentException(stylesheet.toString());
+    	}
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return the cacheControl header to set on all static content.
+     */
+    public String getCacheControl()
+    {
+        return _cacheControl.toString();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param cacheControl the cacheControl header to set on all static content.
+     */
+    public void setCacheControl(String cacheControl)
+    {
+        _cacheControl=cacheControl==null?null:new ByteArrayBuffer(cacheControl);
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     */
+    public Resource getResource(String path) throws MalformedURLException
+    {
+        if (path==null || !path.startsWith("/"))
+            throw new MalformedURLException(path);
+
+        Resource base = _baseResource;
+        if (base==null)
+        {
+            if (_context==null)
+                return null;
+            base=_context.getBaseResource();
+            if (base==null)
+                return null;
+        }
+
+        try
+        {
+            path=URIUtil.canonicalPath(path);
+            return base.addPath(path);
+        }
+        catch(Exception e)
+        {
+            LOG.ignore(e);
+        }
+
+        return null;
+    }
+
+    /* ------------------------------------------------------------ */
+    protected Resource getResource(HttpServletRequest request) throws MalformedURLException
+    {
+        String servletPath;
+        String pathInfo;
+        Boolean included = request.getAttribute(Dispatcher.INCLUDE_REQUEST_URI) != null;
+        if (included != null && included.booleanValue())
+        {
+            servletPath = (String)request.getAttribute(Dispatcher.INCLUDE_SERVLET_PATH);
+            pathInfo = (String)request.getAttribute(Dispatcher.INCLUDE_PATH_INFO);
+ 
+            if (servletPath == null && pathInfo == null)
+            {
+                servletPath = request.getServletPath();
+                pathInfo = request.getPathInfo();
+            }
+        }
+        else
+        {
+            servletPath = request.getServletPath();
+            pathInfo = request.getPathInfo();
+        }
+        
+        String pathInContext=URIUtil.addPaths(servletPath,pathInfo);
+        return getResource(pathInContext);
+    }
+
+
+    /* ------------------------------------------------------------ */
+    public String[] getWelcomeFiles()
+    {
+        return _welcomeFiles;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setWelcomeFiles(String[] welcomeFiles)
+    {
+        _welcomeFiles=welcomeFiles;
+    }
+
+    /* ------------------------------------------------------------ */
+    protected Resource getWelcome(Resource directory) throws MalformedURLException, IOException
+    {
+        for (int i=0;i<_welcomeFiles.length;i++)
+        {
+            Resource welcome=directory.addPath(_welcomeFiles[i]);
+            if (welcome.exists() && !welcome.isDirectory())
+                return welcome;
+        }
+
+        return null;
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.jetty.server.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int)
+     */
+    public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+    {
+        if (baseRequest.isHandled())
+            return;
+
+        boolean skipContentBody = false;
+
+        if(!HttpMethods.GET.equals(request.getMethod()))
+        {
+            if(!HttpMethods.HEAD.equals(request.getMethod()))
+            {
+                //try another handler
+                super.handle(target, baseRequest, request, response);
+                return;
+            }
+            skipContentBody = true;
+        }
+        
+        Resource resource = getResource(request);
+        
+        if (resource==null || !resource.exists())
+        {
+            if (target.endsWith("/jetty-dir.css"))
+            {	                
+                resource = getStylesheet();
+                if (resource==null)
+                    return;
+                response.setContentType("text/css");
+            }
+            else 
+            {
+                //no resource - try other handlers
+                super.handle(target, baseRequest, request, response);
+                return;
+            }
+        }
+            
+        if (!_aliases && resource.getAlias()!=null)
+        {
+            LOG.info(resource+" aliased to "+resource.getAlias());
+            return;
+        }
+
+        // We are going to serve something
+        baseRequest.setHandled(true);
+
+        if (resource.isDirectory())
+        {
+            if (!request.getPathInfo().endsWith(URIUtil.SLASH))
+            {
+                response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getRequestURI(),URIUtil.SLASH)));
+                return;
+            }
+
+            Resource welcome=getWelcome(resource);
+            if (welcome!=null && welcome.exists())
+                resource=welcome;
+            else
+            {
+                doDirectory(request,response,resource);
+                baseRequest.setHandled(true);
+                return;
+            }
+        }
+
+        // set some headers
+        long last_modified=resource.lastModified();
+        String etag=null;
+        if (_etags)
+        {
+            // simple handling of only a single etag
+            String ifnm = request.getHeader(HttpHeaders.IF_NONE_MATCH);
+            etag=resource.getWeakETag();
+            if (ifnm!=null && resource!=null && ifnm.equals(etag))
+            {
+                response.setStatus(HttpStatus.NOT_MODIFIED_304);
+                baseRequest.getResponse().getHttpFields().put(HttpHeaders.ETAG_BUFFER,etag);
+                return;
+            }
+        }
+        
+        
+        if (last_modified>0)
+        {
+            long if_modified=request.getDateHeader(HttpHeaders.IF_MODIFIED_SINCE);
+            if (if_modified>0 && last_modified/1000<=if_modified/1000)
+            {
+                response.setStatus(HttpStatus.NOT_MODIFIED_304);
+                return;
+            }
+        }
+
+        Buffer mime=_mimeTypes.getMimeByExtension(resource.toString());
+        if (mime==null)
+            mime=_mimeTypes.getMimeByExtension(request.getPathInfo());
+
+        // set the headers
+        doResponseHeaders(response,resource,mime!=null?mime.toString():null);
+        response.setDateHeader(HttpHeaders.LAST_MODIFIED,last_modified);
+        if (_etags)
+            baseRequest.getResponse().getHttpFields().put(HttpHeaders.ETAG_BUFFER,etag);
+        
+        if(skipContentBody)
+            return;
+        // Send the content
+        OutputStream out =null;
+        try {out = response.getOutputStream();}
+        catch(IllegalStateException e) {out = new WriterOutputStream(response.getWriter());}
+
+        // See if a short direct method can be used?
+        if (out instanceof AbstractHttpConnection.Output)
+        {
+            // TODO file mapped buffers
+            ((AbstractHttpConnection.Output)out).sendContent(resource.getInputStream());
+        }
+        else
+        {
+            // Write content normally
+            resource.writeTo(out,0,resource.length());
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    protected void doDirectory(HttpServletRequest request,HttpServletResponse response, Resource resource)
+        throws IOException
+    {
+        if (_directory)
+        {
+            String listing = resource.getListHTML(request.getRequestURI(),request.getPathInfo().lastIndexOf("/") > 0);
+            response.setContentType("text/html; charset=UTF-8");
+            response.getWriter().println(listing);
+        }
+        else
+            response.sendError(HttpStatus.FORBIDDEN_403);
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set the response headers.
+     * This method is called to set the response headers such as content type and content length.
+     * May be extended to add additional headers.
+     * @param response
+     * @param resource
+     * @param mimeType
+     */
+    protected void doResponseHeaders(HttpServletResponse response, Resource resource, String mimeType)
+    {
+        if (mimeType!=null)
+            response.setContentType(mimeType);
+
+        long length=resource.length();
+
+        if (response instanceof Response)
+        {
+            HttpFields fields = ((Response)response).getHttpFields();
+
+            if (length>0)
+                fields.putLongField(HttpHeaders.CONTENT_LENGTH_BUFFER,length);
+
+            if (_cacheControl!=null)
+                fields.put(HttpHeaders.CACHE_CONTROL_BUFFER,_cacheControl);
+        }
+        else
+        {
+            if (length>0)
+                response.setHeader(HttpHeaders.CONTENT_LENGTH,Long.toString(length));
+
+            if (_cacheControl!=null)
+                response.setHeader(HttpHeaders.CACHE_CONTROL,_cacheControl.toString());
+        }
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/handler/ScopedHandler.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,193 @@
+//
+//  ========================================================================
+//  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.server.handler;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.server.Request;
+
+
+/* ------------------------------------------------------------ */
+/** ScopedHandler.
+ * 
+ * A ScopedHandler is a HandlerWrapper where the wrapped handlers
+ * each define a scope.   When {@link #handle(String, Request, HttpServletRequest, HttpServletResponse)}
+ * is called on the first ScopedHandler in a chain of HandlerWrappers,
+ * the {@link #doScope(String, Request, HttpServletRequest, HttpServletResponse)} method is 
+ * called on all contained ScopedHandlers, before the 
+ * {@link #doHandle(String, Request, HttpServletRequest, HttpServletResponse)} method 
+ * is called on all contained handlers.
+ * 
+ * <p>For example if Scoped handlers A, B & C were chained together, then 
+ * the calling order would be:<pre>
+ * A.handle(...)
+ *   A.doScope(...)
+ *     B.doScope(...)
+ *       C.doScope(...)
+ *         A.doHandle(...)
+ *           B.doHandle(...)
+ *              C.doHandle(...)   
+ * <pre>
+ * 
+ * <p>If non scoped handler X was in the chained A, B, X & C, then 
+ * the calling order would be:<pre>
+ * A.handle(...)
+ *   A.doScope(...)
+ *     B.doScope(...)
+ *       C.doScope(...)
+ *         A.doHandle(...)
+ *           B.doHandle(...)
+ *             X.handle(...)
+ *               C.handle(...)
+ *                 C.doHandle(...)   
+ * <pre>
+ * 
+ * <p>A typical usage pattern is:<pre>
+ *     private static class MyHandler extends ScopedHandler
+ *     {
+ *         public void doScope(String target, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ *         {
+ *             try
+ *             {
+ *                 setUpMyScope();
+ *                 super.doScope(target,request,response);
+ *             }
+ *             finally
+ *             {
+ *                 tearDownMyScope();
+ *             }
+ *         }
+ *         
+ *         public void doHandle(String target, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ *         {
+ *             try
+ *             {
+ *                 doMyHandling();
+ *                 super.doHandle(target,request,response);
+ *             }
+ *             finally
+ *             {
+ *                 cleanupMyHandling();
+ *             }
+ *         }
+ *     }
+ * </pre>
+ */
+public abstract class ScopedHandler extends HandlerWrapper
+{
+    private static final ThreadLocal<ScopedHandler> __outerScope= new ThreadLocal<ScopedHandler>();
+    protected ScopedHandler _outerScope;
+    protected ScopedHandler _nextScope;
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.handler.HandlerWrapper#doStart()
+     */
+    @Override
+    protected void doStart() throws Exception
+    {
+        try
+        {
+            _outerScope=__outerScope.get();
+            if (_outerScope==null)
+                __outerScope.set(this);
+            
+            super.doStart();
+            
+            _nextScope= (ScopedHandler)getChildHandlerByClass(ScopedHandler.class);
+            
+        }
+        finally
+        {
+            if (_outerScope==null)
+                __outerScope.set(null);
+        }
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /* 
+     */
+    @Override
+    public final void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+    {
+        if (_outerScope==null)  
+            doScope(target,baseRequest,request, response);
+        else 
+            doHandle(target,baseRequest,request, response);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /* 
+     * Scope the handler
+     */
+    public abstract void doScope(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) 
+        throws IOException, ServletException;
+    
+    /* ------------------------------------------------------------ */
+    /* 
+     * Scope the handler
+     */
+    public final void nextScope(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) 
+        throws IOException, ServletException
+    {
+        // this method has been manually inlined in several locations, but
+        // is called protected by an if(never()), so your IDE can find those
+        // locations if this code is changed.
+        if (_nextScope!=null)
+            _nextScope.doScope(target,baseRequest,request, response);
+        else if (_outerScope!=null)
+            _outerScope.doHandle(target,baseRequest,request, response);
+        else 
+            doHandle(target,baseRequest,request, response);
+    }
+
+    /* ------------------------------------------------------------ */
+    /* 
+     * Do the handler work within the scope.
+     */
+    public abstract void doHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) 
+        throws IOException, ServletException;
+    
+    /* ------------------------------------------------------------ */
+    /* 
+     * Do the handler work within the scope.
+     */
+    public final void nextHandle(String target, final Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+    {
+        // this method has been manually inlined in several locations, but
+        // is called protected by an if(never()), so your IDE can find those
+        // locations if this code is changed.
+        if (_nextScope!=null && _nextScope==_handler)
+            _nextScope.doHandle(target,baseRequest,request, response);
+        else if (_handler!=null)
+            _handler.handle(target,baseRequest, request, response);
+    }
+    
+    /* ------------------------------------------------------------ */
+    protected boolean never()
+    {
+        return false;
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/handler/ShutdownHandler.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,170 @@
+//
+//  ========================================================================
+//  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.server.handler;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/* ------------------------------------------------------------ */
+/**
+ * A handler that shuts the server down on a valid request. Used to do "soft" restarts from Java. If _exitJvm ist set to true a hard System.exit() call is being
+ * made.
+ *
+ * This handler is a contribution from Johannes Brodwall: https://bugs.eclipse.org/bugs/show_bug.cgi?id=357687
+ *
+ * Usage:
+ *
+ * <pre>
+    Server server = new Server(8080);
+    HandlerList handlers = new HandlerList();
+    handlers.setHandlers(new Handler[]
+    { someOtherHandler, new ShutdownHandler(server,&quot;secret password&quot;) });
+    server.setHandler(handlers);
+    server.start();
+   </pre>
+ * 
+   <pre>
+   public static void attemptShutdown(int port, String shutdownCookie) {
+        try {
+            URL url = new URL("http://localhost:" + port + "/shutdown?token=" + shutdownCookie);
+            HttpURLConnection connection = (HttpURLConnection)url.openConnection();
+            connection.setRequestMethod("POST");
+            connection.getResponseCode();
+            logger.info("Shutting down " + url + ": " + connection.getResponseMessage());
+        } catch (SocketException e) {
+            logger.debug("Not running");
+            // Okay - the server is not running
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+  </pre>
+ */
+public class ShutdownHandler extends AbstractHandler
+{
+    private static final Logger LOG = Log.getLogger(ShutdownHandler.class);
+
+    private final String _shutdownToken;
+
+    private final Server _server;
+
+    private boolean _exitJvm = false;
+  
+    
+
+    /**
+     * Creates a listener that lets the server be shut down remotely (but only from localhost).
+     *
+     * @param server
+     *            the Jetty instance that should be shut down
+     * @param shutdownToken
+     *            a secret password to avoid unauthorized shutdown attempts
+     */
+    public ShutdownHandler(Server server, String shutdownToken)
+    {
+        this._server = server;
+        this._shutdownToken = shutdownToken;
+    }
+
+    public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+    {
+        if (!target.equals("/shutdown"))
+        {
+            return;
+        }
+
+        if (!request.getMethod().equals("POST"))
+        {
+            response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+            return;
+        }
+        if (!hasCorrectSecurityToken(request))
+        {
+            LOG.warn("Unauthorized shutdown attempt from " + getRemoteAddr(request));
+            response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
+            return;
+        }
+        if (!requestFromLocalhost(request))
+        {
+            LOG.warn("Unauthorized shutdown attempt from " + getRemoteAddr(request));
+            response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
+            return;
+        }
+
+        LOG.info("Shutting down by request from " + getRemoteAddr(request));
+        
+        new Thread()
+        {
+            public void run ()
+            {
+                try
+                {
+                    shutdownServer();
+                }
+                catch (InterruptedException e)
+                {
+                    LOG.ignore(e);
+                }
+                catch (Exception e)
+                {
+                    throw new RuntimeException("Shutting down server",e);
+                }
+            }
+        }.start();
+    }
+
+    private boolean requestFromLocalhost(HttpServletRequest request)
+    {
+        return "127.0.0.1".equals(getRemoteAddr(request));
+    }
+
+    protected String getRemoteAddr(HttpServletRequest request)
+    {
+        return request.getRemoteAddr();
+    }
+
+    private boolean hasCorrectSecurityToken(HttpServletRequest request)
+    {
+        return _shutdownToken.equals(request.getParameter("token"));
+    }
+
+    private void shutdownServer() throws Exception
+    {
+        _server.stop();
+        
+        if (_exitJvm)
+        {
+            System.exit(0);
+        }
+    }
+
+    public void setExitJvm(boolean exitJvm)
+    {
+        this._exitJvm = exitJvm;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/handler/StatisticsHandler.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,474 @@
+//
+//  ========================================================================
+//  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.server.handler;
+
+import java.io.IOException;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.continuation.Continuation;
+import org.eclipse.jetty.continuation.ContinuationListener;
+import org.eclipse.jetty.server.AsyncContinuation;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.util.statistic.CounterStatistic;
+import org.eclipse.jetty.util.statistic.SampleStatistic;
+
+public class StatisticsHandler extends HandlerWrapper
+{
+    private final AtomicLong _statsStartedAt = new AtomicLong();
+    
+    private final CounterStatistic _requestStats = new CounterStatistic();
+    private final SampleStatistic _requestTimeStats = new SampleStatistic();
+    private final CounterStatistic _dispatchedStats = new CounterStatistic();
+    private final SampleStatistic _dispatchedTimeStats = new SampleStatistic();
+    private final CounterStatistic _suspendStats = new CounterStatistic();
+
+    private final AtomicInteger _resumes = new AtomicInteger();
+    private final AtomicInteger _expires = new AtomicInteger();
+    
+    private final AtomicInteger _responses1xx = new AtomicInteger();
+    private final AtomicInteger _responses2xx = new AtomicInteger();
+    private final AtomicInteger _responses3xx = new AtomicInteger();
+    private final AtomicInteger _responses4xx = new AtomicInteger();
+    private final AtomicInteger _responses5xx = new AtomicInteger();
+    private final AtomicLong _responsesTotalBytes = new AtomicLong();
+
+    private final ContinuationListener _onCompletion = new ContinuationListener()
+    {
+        public void onComplete(Continuation continuation)
+        {
+            final Request request = ((AsyncContinuation)continuation).getBaseRequest();
+            final long elapsed = System.currentTimeMillis()-request.getTimeStamp();
+            
+            _requestStats.decrement();
+            _requestTimeStats.set(elapsed);
+            
+            updateResponse(request);
+            
+            if (!continuation.isResumed())
+                _suspendStats.decrement();
+        }
+
+        public void onTimeout(Continuation continuation)
+        {
+            _expires.incrementAndGet();
+        }
+    };
+    
+    /**
+     * Resets the current request statistics.
+     */
+    public void statsReset()
+    {
+        _statsStartedAt.set(System.currentTimeMillis());
+        
+        _requestStats.reset();
+        _requestTimeStats.reset();
+        _dispatchedStats.reset();
+        _dispatchedTimeStats.reset();
+        _suspendStats.reset();
+
+        _resumes.set(0);
+        _expires.set(0);
+        _responses1xx.set(0);
+        _responses2xx.set(0);
+        _responses3xx.set(0);
+        _responses4xx.set(0);
+        _responses5xx.set(0);
+        _responsesTotalBytes.set(0L);
+    }
+
+    @Override
+    public void handle(String path, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException
+    {
+        _dispatchedStats.increment();
+
+        final long start;
+        AsyncContinuation continuation = request.getAsyncContinuation();
+        if (continuation.isInitial())
+        {
+            // new request
+            _requestStats.increment();
+            start = request.getTimeStamp();
+        }
+        else
+        {
+            // resumed request
+            start = System.currentTimeMillis();
+            _suspendStats.decrement();
+            if (continuation.isResumed())
+                _resumes.incrementAndGet();
+        }
+
+        try
+        {
+            super.handle(path, request, httpRequest, httpResponse);
+        }
+        finally
+        {
+            final long now = System.currentTimeMillis();
+            final long dispatched=now-start;
+            
+            _dispatchedStats.decrement();
+            _dispatchedTimeStats.set(dispatched);
+            
+            if (continuation.isSuspended())
+            {
+                if (continuation.isInitial())
+                    continuation.addContinuationListener(_onCompletion);
+                _suspendStats.increment();
+            }
+            else if (continuation.isInitial())
+            {
+                _requestStats.decrement();
+                _requestTimeStats.set(dispatched);
+                updateResponse(request);
+            }
+            // else onCompletion will handle it.
+        }
+    }
+
+    private void updateResponse(Request request)
+    {
+        Response response = request.getResponse();
+        switch (response.getStatus() / 100)
+        {
+            case 1:
+                _responses1xx.incrementAndGet();
+                break;
+            case 2:
+                _responses2xx.incrementAndGet();
+                break;
+            case 3:
+                _responses3xx.incrementAndGet();
+                break;
+            case 4:
+                _responses4xx.incrementAndGet();
+                break;
+            case 5:
+                _responses5xx.incrementAndGet();
+                break;
+            default:
+                break;
+        }
+        _responsesTotalBytes.addAndGet(response.getContentCount());
+    }
+
+    @Override
+    protected void doStart() throws Exception
+    {
+        super.doStart();
+        statsReset();
+    }
+
+    /**
+     * @return the number of requests handled by this handler
+     * since {@link #statsReset()} was last called, excluding
+     * active requests
+     * @see #getResumes()
+     */
+    public int getRequests()
+    {
+        return (int)_requestStats.getTotal();
+    }
+
+    /**
+     * @return the number of requests currently active.
+     * since {@link #statsReset()} was last called.
+     */
+    public int getRequestsActive()
+    {
+        return (int)_requestStats.getCurrent();
+    }
+
+    /**
+     * @return the maximum number of active requests
+     * since {@link #statsReset()} was last called.
+     */
+    public int getRequestsActiveMax()
+    {
+        return (int)_requestStats.getMax();
+    }
+
+    /**
+     * @return the maximum time (in milliseconds) of request handling
+     * since {@link #statsReset()} was last called.
+     */
+    public long getRequestTimeMax()
+    {
+        return _requestTimeStats.getMax();
+    }
+
+    /**
+     * @return the total time (in milliseconds) of requests handling
+     * since {@link #statsReset()} was last called.
+     */
+    public long getRequestTimeTotal()
+    {
+        return _requestTimeStats.getTotal();
+    }
+
+    /**
+     * @return the mean time (in milliseconds) of request handling
+     * since {@link #statsReset()} was last called.
+     * @see #getRequestTimeTotal()
+     * @see #getRequests()
+     */
+    public double getRequestTimeMean()
+    {
+        return _requestTimeStats.getMean();
+    }
+
+    /**
+     * @return the standard deviation of time (in milliseconds) of request handling
+     * since {@link #statsReset()} was last called.
+     * @see #getRequestTimeTotal()
+     * @see #getRequests()
+     */
+    public double getRequestTimeStdDev()
+    {
+        return _requestTimeStats.getStdDev();
+    }
+
+    /**
+     * @return the number of dispatches seen by this handler
+     * since {@link #statsReset()} was last called, excluding
+     * active dispatches
+     */
+    public int getDispatched()
+    {
+        return (int)_dispatchedStats.getTotal();
+    }
+
+    /**
+     * @return the number of dispatches currently in this handler
+     * since {@link #statsReset()} was last called, including
+     * resumed requests
+     */
+    public int getDispatchedActive()
+    {
+        return (int)_dispatchedStats.getCurrent();
+    }
+
+    /**
+     * @return the max number of dispatches currently in this handler
+     * since {@link #statsReset()} was last called, including
+     * resumed requests
+     */
+    public int getDispatchedActiveMax()
+    {
+        return (int)_dispatchedStats.getMax();
+    }
+
+    /**
+     * @return the maximum time (in milliseconds) of request dispatch
+     * since {@link #statsReset()} was last called.
+     */
+    public long getDispatchedTimeMax()
+    {
+        return _dispatchedTimeStats.getMax();
+    }
+    
+    /**
+     * @return the total time (in milliseconds) of requests handling
+     * since {@link #statsReset()} was last called.
+     */
+    public long getDispatchedTimeTotal()
+    {
+        return _dispatchedTimeStats.getTotal();
+    }
+
+    /**
+     * @return the mean time (in milliseconds) of request handling
+     * since {@link #statsReset()} was last called.
+     * @see #getRequestTimeTotal()
+     * @see #getRequests()
+     */
+    public double getDispatchedTimeMean()
+    {
+        return _dispatchedTimeStats.getMean();
+    }
+    
+    /**
+     * @return the standard deviation of time (in milliseconds) of request handling
+     * since {@link #statsReset()} was last called.
+     * @see #getRequestTimeTotal()
+     * @see #getRequests()
+     */
+    public double getDispatchedTimeStdDev()
+    {
+        return _dispatchedTimeStats.getStdDev();
+    }
+    
+    /**
+     * @return the number of requests handled by this handler
+     * since {@link #statsReset()} was last called, including
+     * resumed requests
+     * @see #getResumes()
+     */
+    public int getSuspends()
+    {
+        return (int)_suspendStats.getTotal();
+    }
+
+    /**
+     * @return the number of requests currently suspended.
+     * since {@link #statsReset()} was last called.
+     */
+    public int getSuspendsActive()
+    {
+        return (int)_suspendStats.getCurrent();
+    }
+
+    /**
+     * @return the maximum number of current suspended requests
+     * since {@link #statsReset()} was last called.
+     */
+    public int getSuspendsActiveMax()
+    {
+        return (int)_suspendStats.getMax();
+    }
+    
+    /**
+     * @return the number of requests that have been resumed
+     * @see #getExpires()
+     */
+    public int getResumes()
+    {
+        return _resumes.get();
+    }
+
+    /**
+     * @return the number of requests that expired while suspended.
+     * @see #getResumes()
+     */
+    public int getExpires()
+    {
+        return _expires.get();
+    }
+
+    /**
+     * @return the number of responses with a 1xx status returned by this context
+     * since {@link #statsReset()} was last called.
+     */
+    public int getResponses1xx()
+    {
+        return _responses1xx.get();
+    }
+
+    /**
+     * @return the number of responses with a 2xx status returned by this context
+     * since {@link #statsReset()} was last called.
+     */
+    public int getResponses2xx()
+    {
+        return _responses2xx.get();
+    }
+
+    /**
+     * @return the number of responses with a 3xx status returned by this context
+     * since {@link #statsReset()} was last called.
+     */
+    public int getResponses3xx()
+    {
+        return _responses3xx.get();
+    }
+
+    /**
+     * @return the number of responses with a 4xx status returned by this context
+     * since {@link #statsReset()} was last called.
+     */
+    public int getResponses4xx()
+    {
+        return _responses4xx.get();
+    }
+
+    /**
+     * @return the number of responses with a 5xx status returned by this context
+     * since {@link #statsReset()} was last called.
+     */
+    public int getResponses5xx()
+    {
+        return _responses5xx.get();
+    }
+
+    /**
+     * @return the milliseconds since the statistics were started with {@link #statsReset()}.
+     */
+    public long getStatsOnMs()
+    {
+        return System.currentTimeMillis() - _statsStartedAt.get();
+    }
+    
+    /**
+     * @return the total bytes of content sent in responses
+     */
+    public long getResponsesBytesTotal()
+    {
+        return _responsesTotalBytes.get();
+    }
+    
+    public String toStatsHTML()
+    {   
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("<h1>Statistics:</h1>\n");
+        sb.append("Statistics gathering started ").append(getStatsOnMs()).append("ms ago").append("<br />\n");
+
+        sb.append("<h2>Requests:</h2>\n");
+        sb.append("Total requests: ").append(getRequests()).append("<br />\n");
+        sb.append("Active requests: ").append(getRequestsActive()).append("<br />\n");
+        sb.append("Max active requests: ").append(getRequestsActiveMax()).append("<br />\n");
+        sb.append("Total requests time: ").append(getRequestTimeTotal()).append("<br />\n");
+        sb.append("Mean request time: ").append(getRequestTimeMean()).append("<br />\n");
+        sb.append("Max request time: ").append(getRequestTimeMax()).append("<br />\n");
+        sb.append("Request time standard deviation: ").append(getRequestTimeStdDev()).append("<br />\n");
+        
+
+        sb.append("<h2>Dispatches:</h2>\n");
+        sb.append("Total dispatched: ").append(getDispatched()).append("<br />\n");
+        sb.append("Active dispatched: ").append(getDispatchedActive()).append("<br />\n");
+        sb.append("Max active dispatched: ").append(getDispatchedActiveMax()).append("<br />\n");
+        sb.append("Total dispatched time: ").append(getDispatchedTimeTotal()).append("<br />\n");
+        sb.append("Mean dispatched time: ").append(getDispatchedTimeMean()).append("<br />\n");
+        sb.append("Max dispatched time: ").append(getDispatchedTimeMax()).append("<br />\n");
+        sb.append("Dispatched time standard deviation: ").append(getDispatchedTimeStdDev()).append("<br />\n");
+
+
+        sb.append("Total requests suspended: ").append(getSuspends()).append("<br />\n");
+        sb.append("Total requests expired: ").append(getExpires()).append("<br />\n");
+        sb.append("Total requests resumed: ").append(getResumes()).append("<br />\n");
+        
+        sb.append("<h2>Responses:</h2>\n");
+        sb.append("1xx responses: ").append(getResponses1xx()).append("<br />\n");
+        sb.append("2xx responses: ").append(getResponses2xx()).append("<br />\n");
+        sb.append("3xx responses: ").append(getResponses3xx()).append("<br />\n");
+        sb.append("4xx responses: ").append(getResponses4xx()).append("<br />\n");
+        sb.append("5xx responses: ").append(getResponses5xx()).append("<br />\n");
+        sb.append("Bytes sent total: ").append(getResponsesBytesTotal()).append("<br />\n");
+
+        return sb.toString();
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/nio/AbstractNIOConnector.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,53 @@
+//
+//  ========================================================================
+//  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.server.nio;
+
+import org.eclipse.jetty.io.Buffers.Type;
+import org.eclipse.jetty.server.AbstractConnector;
+
+public abstract class AbstractNIOConnector extends AbstractConnector implements NIOConnector
+{
+    public AbstractNIOConnector()
+    {
+        _buffers.setRequestBufferType(Type.DIRECT);
+        _buffers.setRequestHeaderType(Type.INDIRECT);
+        _buffers.setResponseBufferType(Type.DIRECT);
+        _buffers.setResponseHeaderType(Type.INDIRECT);
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    public boolean getUseDirectBuffers()
+    {
+        return getRequestBufferType()==Type.DIRECT;
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    /**
+     * @param direct If True (the default), the connector can use NIO direct buffers.
+     * Some JVMs have memory management issues (bugs) with direct buffers.
+     */
+    public void setUseDirectBuffers(boolean direct)
+    {
+        _buffers.setRequestBufferType(direct?Type.DIRECT:Type.INDIRECT);
+        _buffers.setResponseBufferType(direct?Type.DIRECT:Type.INDIRECT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/nio/BlockingChannelConnector.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,366 @@
+//
+//  ========================================================================
+//  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.server.nio;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.nio.channels.ByteChannel;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+import java.util.Set;
+
+import org.eclipse.jetty.http.HttpException;
+import org.eclipse.jetty.io.Buffer;
+import org.eclipse.jetty.io.ConnectedEndPoint;
+import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.EofException;
+import org.eclipse.jetty.io.nio.ChannelEndPoint;
+import org.eclipse.jetty.server.BlockingHttpConnection;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.util.ConcurrentHashSet;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+
+/* ------------------------------------------------------------------------------- */
+/**  Blocking NIO connector.
+ * This connector uses efficient NIO buffers with a traditional blocking thread model.
+ * Direct NIO buffers are used and a thread is allocated per connections.
+ *
+ * This connector is best used when there are a few very active connections.
+ *
+ * @org.apache.xbean.XBean element="blockingNioConnector" description="Creates a blocking NIO based socket connector"
+ *
+ *
+ *
+ */
+public class BlockingChannelConnector extends AbstractNIOConnector
+{
+    private static final Logger LOG = Log.getLogger(BlockingChannelConnector.class);
+
+    private transient ServerSocketChannel _acceptChannel;
+    private final Set<BlockingChannelEndPoint> _endpoints = new ConcurrentHashSet<BlockingChannelEndPoint>();
+
+
+    /* ------------------------------------------------------------ */
+    /** Constructor.
+     *
+     */
+    public BlockingChannelConnector()
+    {
+    }
+
+    /* ------------------------------------------------------------ */
+    public Object getConnection()
+    {
+        return _acceptChannel;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.AbstractConnector#doStart()
+     */
+    @Override
+    protected void doStart() throws Exception
+    {
+        super.doStart();
+        getThreadPool().dispatch(new Runnable()
+        {
+
+            public void run()
+            {
+                while (isRunning())
+                {
+                    try
+                    {
+                        Thread.sleep(400);
+                        long now=System.currentTimeMillis();
+                        for (BlockingChannelEndPoint endp : _endpoints)
+                        {
+                            endp.checkIdleTimestamp(now);
+                        }
+                    }
+                    catch(InterruptedException e)
+                    {
+                        LOG.ignore(e);
+                    }
+                    catch(Exception e)
+                    {
+                        LOG.warn(e);
+                    }
+                }
+            }
+
+        });
+
+    }
+
+
+    /* ------------------------------------------------------------ */
+    public void open() throws IOException
+    {
+        // Create a new server socket and set to non blocking mode
+        _acceptChannel= ServerSocketChannel.open();
+        _acceptChannel.configureBlocking(true);
+
+        // Bind the server socket to the local host and port
+        InetSocketAddress addr = getHost()==null?new InetSocketAddress(getPort()):new InetSocketAddress(getHost(),getPort());
+        _acceptChannel.socket().bind(addr,getAcceptQueueSize());
+    }
+
+    /* ------------------------------------------------------------ */
+    public void close() throws IOException
+    {
+        if (_acceptChannel != null)
+            _acceptChannel.close();
+        _acceptChannel=null;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void accept(int acceptorID)
+    	throws IOException, InterruptedException
+    {
+        SocketChannel channel = _acceptChannel.accept();
+        channel.configureBlocking(true);
+        Socket socket=channel.socket();
+        configure(socket);
+
+        BlockingChannelEndPoint connection=new BlockingChannelEndPoint(channel);
+        connection.dispatch();
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    @Override
+    public void customize(EndPoint endpoint, Request request)
+        throws IOException
+    {
+        super.customize(endpoint, request);
+        endpoint.setMaxIdleTime(_maxIdleTime);
+        configure(((SocketChannel)endpoint.getTransport()).socket());
+    }
+
+
+    /* ------------------------------------------------------------------------------- */
+    public int getLocalPort()
+    {
+        if (_acceptChannel==null || !_acceptChannel.isOpen())
+            return -1;
+        return _acceptChannel.socket().getLocalPort();
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    /* ------------------------------------------------------------------------------- */
+    /* ------------------------------------------------------------------------------- */
+    private class BlockingChannelEndPoint extends ChannelEndPoint implements Runnable, ConnectedEndPoint
+    {
+        private Connection _connection;
+        private int _timeout;
+        private volatile long _idleTimestamp;
+
+        BlockingChannelEndPoint(ByteChannel channel)
+            throws IOException
+        {
+            super(channel,BlockingChannelConnector.this._maxIdleTime);
+            _connection = new BlockingHttpConnection(BlockingChannelConnector.this,this,getServer());
+        }
+
+        /* ------------------------------------------------------------ */
+        /** Get the connection.
+         * @return the connection
+         */
+        public Connection getConnection()
+        {
+            return _connection;
+        }
+
+        /* ------------------------------------------------------------ */
+        public void setConnection(Connection connection)
+        {
+            _connection=connection;
+        }
+
+        /* ------------------------------------------------------------ */
+        public void checkIdleTimestamp(long now)
+        {
+            if (_idleTimestamp!=0 && _timeout>0 && now>(_idleTimestamp+_timeout))
+            {
+                idleExpired();
+            }
+        }
+
+        /* ------------------------------------------------------------ */
+        protected void idleExpired()
+        {
+            try
+            {
+                super.close();
+            }
+            catch (IOException e)
+            {
+                LOG.ignore(e);
+            }
+        }
+
+        /* ------------------------------------------------------------ */
+        void dispatch() throws IOException
+        {
+            if (!getThreadPool().dispatch(this))
+            {
+                LOG.warn("dispatch failed for  {}",_connection);
+                super.close();
+            }
+        }
+
+        /* ------------------------------------------------------------ */
+        /**
+         * @see org.eclipse.jetty.io.nio.ChannelEndPoint#fill(org.eclipse.jetty.io.Buffer)
+         */
+        @Override
+        public int fill(Buffer buffer) throws IOException
+        {
+            _idleTimestamp=System.currentTimeMillis();
+            return super.fill(buffer);
+        }
+
+        /* ------------------------------------------------------------ */
+        /**
+         * @see org.eclipse.jetty.io.nio.ChannelEndPoint#flush(org.eclipse.jetty.io.Buffer)
+         */
+        @Override
+        public int flush(Buffer buffer) throws IOException
+        {
+            _idleTimestamp=System.currentTimeMillis();
+            return super.flush(buffer);
+        }
+
+        /* ------------------------------------------------------------ */
+        /**
+         * @see org.eclipse.jetty.io.nio.ChannelEndPoint#flush(org.eclipse.jetty.io.Buffer, org.eclipse.jetty.io.Buffer, org.eclipse.jetty.io.Buffer)
+         */
+        @Override
+        public int flush(Buffer header, Buffer buffer, Buffer trailer) throws IOException
+        {
+            _idleTimestamp=System.currentTimeMillis();
+            return super.flush(header,buffer,trailer);
+        }
+
+        /* ------------------------------------------------------------ */
+        public void run()
+        {
+            try
+            {
+                _timeout=getMaxIdleTime();
+                connectionOpened(_connection);
+                _endpoints.add(this);
+
+                while (isOpen())
+                {
+                    _idleTimestamp=System.currentTimeMillis();
+                    if (_connection.isIdle())
+                    {
+                        if (getServer().getThreadPool().isLowOnThreads())
+                        {
+                            int lrmit = getLowResourcesMaxIdleTime();
+                            if (lrmit>=0 && _timeout!= lrmit)
+                            {
+                                _timeout=lrmit;
+                            }
+                        }
+                    }
+                    else
+                    {
+                        if (_timeout!=getMaxIdleTime())
+                        {
+                            _timeout=getMaxIdleTime();
+                        }
+                    }
+
+                    _connection = _connection.handle();
+
+                }
+            }
+            catch (EofException e)
+            {
+                LOG.debug("EOF", e);
+                try{BlockingChannelEndPoint.this.close();}
+                catch(IOException e2){LOG.ignore(e2);}
+            }
+            catch (HttpException e)
+            {
+                LOG.debug("BAD", e);
+                try{super.close();}
+                catch(IOException e2){LOG.ignore(e2);}
+            }
+            catch(Throwable e)
+            {
+                LOG.warn("handle failed",e);
+                try{super.close();}
+                catch(IOException e2){LOG.ignore(e2);}
+            }
+            finally
+            {
+                connectionClosed(_connection);
+                _endpoints.remove(this);
+
+                // wait for client to close, but if not, close ourselves.
+                try
+                {
+                    if (!_socket.isClosed())
+                    {
+                        long timestamp=System.currentTimeMillis();
+                        int max_idle=getMaxIdleTime();
+
+                        _socket.setSoTimeout(getMaxIdleTime());
+                        int c=0;
+                        do
+                        {
+                            c = _socket.getInputStream().read();
+                        }
+                        while (c>=0 && (System.currentTimeMillis()-timestamp)<max_idle);
+                        if (!_socket.isClosed())
+                            _socket.close();
+                    }
+                }
+                catch(IOException e)
+                {
+                    LOG.ignore(e);
+                }
+            }
+        }
+
+        /* ------------------------------------------------------------ */
+        @Override
+        public String toString()
+        {
+            return String.format("BCEP@%x{l(%s)<->r(%s),open=%b,ishut=%b,oshut=%b}-{%s}",
+                    hashCode(),
+                    _socket.getRemoteSocketAddress(),
+                    _socket.getLocalSocketAddress(),
+                    isOpen(),
+                    isInputShutdown(),
+                    isOutputShutdown(),
+                    _connection);
+        }
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/nio/InheritedChannelConnector.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,75 @@
+//
+//  ========================================================================
+//  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.server.nio;
+
+import java.io.IOException;
+import java.nio.channels.Channel;
+import java.nio.channels.ServerSocketChannel;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * An implementation of the SelectChannelConnector which first tries to  
+ * inherit from a channel provided by the system. If there is no inherited
+ * channel available, or if the inherited channel provided not usable, then 
+ * it will fall back upon normal ServerSocketChannel creation.
+ * <p> 
+ * Note that System.inheritedChannel() is only available from Java 1.5 onwards.
+ * Trying to use this class under Java 1.4 will be the same as using a normal
+ * SelectChannelConnector. 
+ * <p> 
+ * Use it with xinetd/inetd, to launch an instance of Jetty on demand. The port
+ * used to access pages on the Jetty instance is the same as the port used to
+ * launch Jetty. 
+ * 
+ * @author athena
+ */
+public class InheritedChannelConnector extends SelectChannelConnector
+{
+    private static final Logger LOG = Log.getLogger(InheritedChannelConnector.class);
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void open() throws IOException
+    {
+        synchronized(this)
+        {
+            try 
+            {
+                Channel channel = System.inheritedChannel();
+                if ( channel instanceof ServerSocketChannel )
+                    _acceptChannel = (ServerSocketChannel)channel;
+                else
+                    LOG.warn("Unable to use System.inheritedChannel() [" +channel+ "]. Trying a new ServerSocketChannel at " + getHost() + ":" + getPort());
+                
+                if ( _acceptChannel != null )
+                    _acceptChannel.configureBlocking(true);
+            }
+            catch(NoSuchMethodError e)
+            {
+                LOG.warn("Need at least Java 5 to use socket inherited from xinetd/inetd.");
+            }
+
+            if (_acceptChannel == null)
+                super.open();
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/nio/NIOConnector.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,31 @@
+//
+//  ========================================================================
+//  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.server.nio; 
+
+/** 
+ * NIOConnector.
+ * A marker interface that indicates that NIOBuffers can be handled (efficiently) by this Connector.
+ * 
+ * 
+ * 
+ */
+public interface NIOConnector
+{
+    boolean getUseDirectBuffers();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/nio/NetworkTrafficSelectChannelConnector.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,73 @@
+//
+//  ========================================================================
+//  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.server.nio;
+
+import java.io.IOException;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.SocketChannel;
+import java.util.ConcurrentModificationException;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.eclipse.jetty.io.NetworkTrafficListener;
+import org.eclipse.jetty.io.nio.NetworkTrafficSelectChannelEndPoint;
+import org.eclipse.jetty.io.nio.SelectChannelEndPoint;
+import org.eclipse.jetty.io.nio.SelectorManager;
+
+/**
+ * <p>A specialized version of {@link SelectChannelConnector} that supports {@link NetworkTrafficListener}s.</p>
+ * <p>{@link NetworkTrafficListener}s can be added and removed dynamically before and after this connector has
+ * been started without causing {@link ConcurrentModificationException}s.</p>
+ */
+public class NetworkTrafficSelectChannelConnector extends SelectChannelConnector
+{
+    private final List<NetworkTrafficListener> listeners = new CopyOnWriteArrayList<NetworkTrafficListener>();
+
+    /**
+     * @param listener the listener to add
+     */
+    public void addNetworkTrafficListener(NetworkTrafficListener listener)
+    {
+        listeners.add(listener);
+    }
+
+    /**
+     * @param listener the listener to remove
+     */
+    public void removeNetworkTrafficListener(NetworkTrafficListener listener)
+    {
+        listeners.remove(listener);
+    }
+
+    @Override
+    protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectorManager.SelectSet selectSet, SelectionKey key) throws IOException
+    {
+        NetworkTrafficSelectChannelEndPoint endPoint = new NetworkTrafficSelectChannelEndPoint(channel, selectSet, key, _maxIdleTime, listeners);
+        endPoint.setConnection(selectSet.getManager().newConnection(channel,endPoint, key.attachment()));
+        endPoint.notifyOpened();
+        return endPoint;
+    }
+
+    @Override
+    protected void endPointClosed(SelectChannelEndPoint endpoint)
+    {
+        super.endPointClosed(endpoint);
+        ((NetworkTrafficSelectChannelEndPoint)endpoint).notifyClosed();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/nio/SelectChannelConnector.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,334 @@
+//
+//  ========================================================================
+//  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.server.nio;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+
+import org.eclipse.jetty.continuation.Continuation;
+import org.eclipse.jetty.io.AsyncEndPoint;
+import org.eclipse.jetty.io.ConnectedEndPoint;
+import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.nio.AsyncConnection;
+import org.eclipse.jetty.io.nio.SelectChannelEndPoint;
+import org.eclipse.jetty.io.nio.SelectorManager;
+import org.eclipse.jetty.io.nio.SelectorManager.SelectSet;
+import org.eclipse.jetty.server.AsyncHttpConnection;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.util.thread.ThreadPool;
+
+/* ------------------------------------------------------------------------------- */
+/**
+ * Selecting NIO connector.
+ * <p>
+ * This connector uses efficient NIO buffers with a non blocking threading model. Direct NIO buffers
+ * are used and threads are only allocated to connections with requests. Synchronization is used to
+ * simulate blocking for the servlet API, and any unflushed content at the end of request handling
+ * is written asynchronously.
+ * </p>
+ * <p>
+ * This connector is best used when there are a many connections that have idle periods.
+ * </p>
+ * <p>
+ * When used with {@link org.eclipse.jetty.continuation.Continuation}, threadless waits are supported.
+ * If a filter or servlet returns after calling {@link Continuation#suspend()} or when a
+ * runtime exception is thrown from a call to {@link Continuation#undispatch()}, Jetty will
+ * will not send a response to the client. Instead the thread is released and the Continuation is
+ * placed on the timer queue. If the Continuation timeout expires, or it's
+ * resume method is called, then the request is again allocated a thread and the request is retried.
+ * The limitation of this approach is that request content is not available on the retried request,
+ * thus if possible it should be read after the continuation or saved as a request attribute or as the
+ * associated object of the Continuation instance.
+ * </p>
+ *
+ * @org.apache.xbean.XBean element="nioConnector" description="Creates an NIO based socket connector"
+ */
+public class SelectChannelConnector extends AbstractNIOConnector
+{
+    protected ServerSocketChannel _acceptChannel;
+    private int _lowResourcesConnections;
+    private int _lowResourcesMaxIdleTime;
+    private int _localPort=-1;
+
+    private final SelectorManager _manager = new ConnectorSelectorManager();
+
+    /* ------------------------------------------------------------------------------- */
+    /**
+     * Constructor.
+     *
+     */
+    public SelectChannelConnector()
+    {
+        _manager.setMaxIdleTime(getMaxIdleTime());
+        addBean(_manager,true);
+        setAcceptors(Math.max(1,(Runtime.getRuntime().availableProcessors()+3)/4));
+    }
+    
+    @Override
+    public void setThreadPool(ThreadPool pool)
+    {
+        super.setThreadPool(pool);
+        // preserve start order
+        removeBean(_manager);
+        addBean(_manager,true);
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public void accept(int acceptorID) throws IOException
+    {
+        ServerSocketChannel server;
+        synchronized(this)
+        {
+            server = _acceptChannel;
+        }
+
+        if (server!=null && server.isOpen() && _manager.isStarted())
+        {
+            SocketChannel channel = server.accept();
+            channel.configureBlocking(false);
+            Socket socket = channel.socket();
+            configure(socket);
+            _manager.register(channel);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public void close() throws IOException
+    {
+        synchronized(this)
+        {
+            if (_acceptChannel != null)
+            {
+                removeBean(_acceptChannel);
+                if (_acceptChannel.isOpen())
+                    _acceptChannel.close();
+            }
+            _acceptChannel = null;
+            _localPort=-2;
+        }
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    @Override
+    public void customize(EndPoint endpoint, Request request) throws IOException
+    {
+        request.setTimeStamp(System.currentTimeMillis());
+        endpoint.setMaxIdleTime(_maxIdleTime);
+        super.customize(endpoint, request);
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    @Override
+    public void persist(EndPoint endpoint) throws IOException
+    {
+        AsyncEndPoint aEndp = ((AsyncEndPoint)endpoint);
+        aEndp.setCheckForIdle(true);
+        super.persist(endpoint);
+    }
+
+    /* ------------------------------------------------------------ */
+    public SelectorManager getSelectorManager()
+    {
+        return _manager;
+    }
+
+    /* ------------------------------------------------------------ */
+    public synchronized Object getConnection()
+    {
+        return _acceptChannel;
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    public int getLocalPort()
+    {
+        synchronized(this)
+        {
+            return _localPort;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public void open() throws IOException
+    {
+        synchronized(this)
+        {
+            if (_acceptChannel == null)
+            {
+                // Create a new server socket
+                _acceptChannel = ServerSocketChannel.open();
+                // Set to blocking mode
+                _acceptChannel.configureBlocking(true);
+
+                // Bind the server socket to the local host and port
+                _acceptChannel.socket().setReuseAddress(getReuseAddress());
+                InetSocketAddress addr = getHost()==null?new InetSocketAddress(getPort()):new InetSocketAddress(getHost(),getPort());
+                _acceptChannel.socket().bind(addr,getAcceptQueueSize());
+
+                _localPort=_acceptChannel.socket().getLocalPort();
+                if (_localPort<=0)
+                    throw new IOException("Server channel not bound");
+
+                addBean(_acceptChannel);
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void setMaxIdleTime(int maxIdleTime)
+    {
+        _manager.setMaxIdleTime(maxIdleTime);
+        super.setMaxIdleTime(maxIdleTime);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return the lowResourcesConnections
+     */
+    public int getLowResourcesConnections()
+    {
+        return _lowResourcesConnections;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set the number of connections, which if exceeded places this manager in low resources state.
+     * This is not an exact measure as the connection count is averaged over the select sets.
+     * @param lowResourcesConnections the number of connections
+     * @see #setLowResourcesMaxIdleTime(int)
+     */
+    public void setLowResourcesConnections(int lowResourcesConnections)
+    {
+        _lowResourcesConnections=lowResourcesConnections;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return the lowResourcesMaxIdleTime
+     */
+    @Override
+    public int getLowResourcesMaxIdleTime()
+    {
+        return _lowResourcesMaxIdleTime;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set the period in ms that a connection is allowed to be idle when this there are more
+     * than {@link #getLowResourcesConnections()} connections.  This allows the server to rapidly close idle connections
+     * in order to gracefully handle high load situations.
+     * @param lowResourcesMaxIdleTime the period in ms that a connection is allowed to be idle when resources are low.
+     * @see #setMaxIdleTime(int)
+     */
+    @Override
+    public void setLowResourcesMaxIdleTime(int lowResourcesMaxIdleTime)
+    {
+        _lowResourcesMaxIdleTime=lowResourcesMaxIdleTime;
+        super.setLowResourcesMaxIdleTime(lowResourcesMaxIdleTime);
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.jetty.server.server.AbstractConnector#doStart()
+     */
+    @Override
+    protected void doStart() throws Exception
+    {
+        _manager.setSelectSets(getAcceptors());
+        _manager.setMaxIdleTime(getMaxIdleTime());
+        _manager.setLowResourcesConnections(getLowResourcesConnections());
+        _manager.setLowResourcesMaxIdleTime(getLowResourcesMaxIdleTime());
+
+        super.doStart();
+    }
+
+    /* ------------------------------------------------------------ */
+    protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey key) throws IOException
+    {
+        SelectChannelEndPoint endp= new SelectChannelEndPoint(channel,selectSet,key, SelectChannelConnector.this._maxIdleTime);
+        endp.setConnection(selectSet.getManager().newConnection(channel,endp, key.attachment()));
+        return endp;
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    protected void endPointClosed(SelectChannelEndPoint endpoint)
+    {
+        connectionClosed(endpoint.getConnection());
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    protected AsyncConnection newConnection(SocketChannel channel,final AsyncEndPoint endpoint)
+    {
+        return new AsyncHttpConnection(SelectChannelConnector.this,endpoint,getServer());
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    private final class ConnectorSelectorManager extends SelectorManager
+    {
+        @Override
+        public boolean dispatch(Runnable task)
+        {
+            ThreadPool pool=getThreadPool();
+            if (pool==null)
+                pool=getServer().getThreadPool();
+            return pool.dispatch(task);
+        }
+
+        @Override
+        protected void endPointClosed(final SelectChannelEndPoint endpoint)
+        {
+            SelectChannelConnector.this.endPointClosed(endpoint);
+        }
+
+        @Override
+        protected void endPointOpened(SelectChannelEndPoint endpoint)
+        {
+            // TODO handle max connections and low resources
+            connectionOpened(endpoint.getConnection());
+        }
+
+        @Override
+        protected void endPointUpgraded(ConnectedEndPoint endpoint, Connection oldConnection)
+        {
+            connectionUpgraded(oldConnection,endpoint.getConnection());
+        }
+
+        @Override
+        public AsyncConnection newConnection(SocketChannel channel,AsyncEndPoint endpoint, Object attachment)
+        {
+            return SelectChannelConnector.this.newConnection(channel,endpoint);
+        }
+
+        @Override
+        protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey sKey) throws IOException
+        {
+            return SelectChannelConnector.this.newEndPoint(channel,selectSet,sKey);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/nio/jmx/SelectChannelConnector-mbean.properties	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,2 @@
+SelectChannelConnector: HTTP connector using NIO ByteChannels and Selectors
+lowResourcesConnections: The number of connections, which if exceeded represents low resources
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/session/AbstractSession.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,568 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSessionActivationListener;
+import javax.servlet.http.HttpSessionBindingEvent;
+import javax.servlet.http.HttpSessionBindingListener;
+import javax.servlet.http.HttpSessionContext;
+import javax.servlet.http.HttpSessionEvent;
+
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ *
+ * <p>
+ * Implements {@link javax.servlet.http.HttpSession} from the <code>javax.servlet</code> package.
+ * </p>
+ * 
+ */
+@SuppressWarnings("deprecation")
+public abstract class AbstractSession implements AbstractSessionManager.SessionIf
+{
+    final static Logger LOG = SessionHandler.LOG;
+    
+    private final AbstractSessionManager _manager;
+    private final String _clusterId; // ID unique within cluster
+    private final String _nodeId;    // ID unique within node
+    private final Map<String,Object> _attributes=new HashMap<String, Object>();
+    private boolean _idChanged;
+    private final long _created;
+    private long _cookieSet;
+    private long _accessed;         // the time of the last access
+    private long _lastAccessed;     // the time of the last access excluding this one
+    private boolean _invalid;
+    private boolean _doInvalidate;
+    private long _maxIdleMs;
+    private boolean _newSession;
+    private int _requests;
+
+
+    
+    /* ------------------------------------------------------------- */
+    protected AbstractSession(AbstractSessionManager abstractSessionManager, HttpServletRequest request)
+    {
+        _manager = abstractSessionManager;
+        
+        _newSession=true;
+        _created=System.currentTimeMillis();
+        _clusterId=_manager._sessionIdManager.newSessionId(request,_created);
+        _nodeId=_manager._sessionIdManager.getNodeId(_clusterId,request);
+        _accessed=_created;
+        _lastAccessed=_created;
+        _requests=1;
+        _maxIdleMs=_manager._dftMaxIdleSecs>0?_manager._dftMaxIdleSecs*1000L:-1;
+        if (LOG.isDebugEnabled())
+            LOG.debug("new session & id "+_nodeId+" "+_clusterId);
+    }
+
+    /* ------------------------------------------------------------- */
+    protected AbstractSession(AbstractSessionManager abstractSessionManager, long created, long accessed, String clusterId)
+    {
+        _manager = abstractSessionManager;
+        _created=created;
+        _clusterId=clusterId;
+        _nodeId=_manager._sessionIdManager.getNodeId(_clusterId,null);
+        _accessed=accessed;
+        _lastAccessed=accessed;
+        _requests=1;
+        _maxIdleMs=_manager._dftMaxIdleSecs>0?_manager._dftMaxIdleSecs*1000L:-1;
+        if (LOG.isDebugEnabled())
+            LOG.debug("new session "+_nodeId+" "+_clusterId);
+    }
+    
+    /* ------------------------------------------------------------- */
+    /**
+     * asserts that the session is valid
+     */
+    protected void checkValid() throws IllegalStateException
+    {
+        if (_invalid)
+            throw new IllegalStateException();
+    }
+    
+    /* ------------------------------------------------------------- */
+    public AbstractSession getSession()
+    {
+        return this;
+    }
+
+    /* ------------------------------------------------------------- */
+    public long getAccessed()
+    {
+        synchronized (this)
+        {
+            return _accessed;
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    public Object getAttribute(String name)
+    {
+        synchronized (this)
+        {
+            checkValid();
+            return _attributes.get(name);
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    public int getAttributes()
+    {
+        synchronized (this)
+        {
+            checkValid();
+            return _attributes.size();
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    @SuppressWarnings({ "unchecked" })
+    public Enumeration<String> getAttributeNames()
+    {
+        synchronized (this)
+        {
+            checkValid();
+            List<String> names=_attributes==null?Collections.EMPTY_LIST:new ArrayList<String>(_attributes.keySet());
+            return Collections.enumeration(names);
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    public Set<String> getNames()
+    {
+        synchronized (this)
+        { 
+            return new HashSet<String>(_attributes.keySet());
+        }
+    }
+
+    /* ------------------------------------------------------------- */
+    public long getCookieSetTime()
+    {
+        return _cookieSet;
+    }
+
+    /* ------------------------------------------------------------- */
+    public long getCreationTime() throws IllegalStateException
+    {
+        return _created;
+    }
+
+    /* ------------------------------------------------------------ */
+    public String getId() throws IllegalStateException
+    {
+        return _manager._nodeIdInSessionId?_nodeId:_clusterId;
+    }
+
+    /* ------------------------------------------------------------- */
+    public String getNodeId()
+    {
+        return _nodeId;
+    }
+
+    /* ------------------------------------------------------------- */
+    public String getClusterId()
+    {
+        return _clusterId;
+    }
+
+    /* ------------------------------------------------------------- */
+    public long getLastAccessedTime() throws IllegalStateException
+    {
+        checkValid();
+        return _lastAccessed;
+    }
+    
+    /* ------------------------------------------------------------- */
+    public void setLastAccessedTime(long time)
+    {
+        _lastAccessed = time;
+    }
+
+    /* ------------------------------------------------------------- */
+    public int getMaxInactiveInterval()
+    {
+        checkValid();
+        return (int)(_maxIdleMs/1000);
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see javax.servlet.http.HttpSession#getServletContext()
+     */
+    public ServletContext getServletContext()
+    {
+        return _manager._context;
+    }
+
+    /* ------------------------------------------------------------- */
+    @Deprecated
+    public HttpSessionContext getSessionContext() throws IllegalStateException
+    {
+        checkValid();
+        return AbstractSessionManager.__nullSessionContext;
+    }
+
+    /* ------------------------------------------------------------- */
+    /**
+     * @deprecated As of Version 2.2, this method is replaced by
+     *             {@link #getAttribute}
+     */
+    @Deprecated
+    public Object getValue(String name) throws IllegalStateException
+    {
+        return getAttribute(name);
+    }
+
+    /* ------------------------------------------------------------- */
+    /**
+     * @deprecated As of Version 2.2, this method is replaced by
+     *             {@link #getAttributeNames}
+     */
+    @Deprecated
+    public String[] getValueNames() throws IllegalStateException
+    {
+        synchronized(this)
+        {
+            checkValid();
+            if (_attributes==null)
+                return new String[0];
+            String[] a=new String[_attributes.size()];
+            return (String[])_attributes.keySet().toArray(a);
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    protected  Map<String,Object> getAttributeMap ()
+    {
+        return _attributes;
+    }
+    
+    /* ------------------------------------------------------------ */
+    protected void addAttributes(Map<String,Object> map)
+    {
+        _attributes.putAll(map);
+    }
+
+    /* ------------------------------------------------------------ */
+    protected boolean access(long time)
+    {
+        synchronized(this)
+        {
+            if (_invalid)
+                return false;
+            _newSession=false;
+            _lastAccessed=_accessed;
+            _accessed=time;
+
+            if (_maxIdleMs>0 && _lastAccessed>0 && _lastAccessed + _maxIdleMs < time) 
+            {
+                invalidate();
+                return false;
+            }
+            _requests++;
+            return true;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    protected void complete()
+    {
+        synchronized(this)
+        {
+            _requests--;
+            if (_doInvalidate && _requests<=0  )
+                doInvalidate();
+        }
+    }
+
+
+    /* ------------------------------------------------------------- */
+    protected void timeout() throws IllegalStateException
+    {
+        // remove session from context and invalidate other sessions with same ID.
+        _manager.removeSession(this,true);
+
+        // Notify listeners and unbind values
+        boolean do_invalidate=false;
+        synchronized (this)
+        {
+            if (!_invalid)
+            {
+                if (_requests<=0)
+                    do_invalidate=true;
+                else
+                    _doInvalidate=true;
+            }
+        }
+        if (do_invalidate)
+            doInvalidate();
+    }
+
+    /* ------------------------------------------------------------- */
+    public void invalidate() throws IllegalStateException
+    {
+        // remove session from context and invalidate other sessions with same ID.
+        _manager.removeSession(this,true);
+        doInvalidate();
+    }
+
+    /* ------------------------------------------------------------- */
+    protected void doInvalidate() throws IllegalStateException
+    {
+        try
+        {
+            LOG.debug("invalidate {}",_clusterId);
+            if (isValid())
+                clearAttributes();
+        }
+        finally
+        {
+            synchronized (this)
+            {
+                // mark as invalid
+                _invalid=true;
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------- */
+    public void clearAttributes() 
+    {
+        while (_attributes!=null && _attributes.size()>0)
+        {
+            ArrayList<String> keys;
+            synchronized(this)
+            {
+                keys=new ArrayList<String>(_attributes.keySet());
+            }
+
+            Iterator<String> iter=keys.iterator();
+            while (iter.hasNext())
+            {
+                String key=(String)iter.next();
+
+                Object value;
+                synchronized(this)
+                {
+                    value=doPutOrRemove(key,null);
+                }
+                unbindValue(key,value);
+
+                _manager.doSessionAttributeListeners(this,key,value,null);
+            }
+        } 
+        if (_attributes!=null)
+            _attributes.clear();
+    }
+    
+    /* ------------------------------------------------------------- */
+    public boolean isIdChanged()
+    {
+        return _idChanged;
+    }
+
+    /* ------------------------------------------------------------- */
+    public boolean isNew() throws IllegalStateException
+    {
+        checkValid();
+        return _newSession;
+    }
+
+    /* ------------------------------------------------------------- */
+    /**
+     * @deprecated As of Version 2.2, this method is replaced by
+     *             {@link #setAttribute}
+     */
+    @Deprecated
+    public void putValue(java.lang.String name, java.lang.Object value) throws IllegalStateException
+    {
+        setAttribute(name,value);
+    }
+
+    /* ------------------------------------------------------------ */
+    public void removeAttribute(String name)
+    {
+        setAttribute(name,null);
+    }
+
+    /* ------------------------------------------------------------- */
+    /**
+     * @deprecated As of Version 2.2, this method is replaced by
+     *             {@link #removeAttribute}
+     */
+    @Deprecated
+    public void removeValue(java.lang.String name) throws IllegalStateException
+    {
+        removeAttribute(name);
+    }
+
+    /* ------------------------------------------------------------ */
+    protected Object doPutOrRemove(String name, Object value)
+    {
+        return value==null?_attributes.remove(name):_attributes.put(name,value);
+    }
+
+    /* ------------------------------------------------------------ */
+    protected Object doGet(String name)
+    {
+        return _attributes.get(name);
+    }
+    
+    /* ------------------------------------------------------------ */
+    public void setAttribute(String name, Object value)
+    {
+        Object old=null;
+        synchronized (this)
+        {
+            checkValid();
+            old=doPutOrRemove(name,value);
+        }
+        
+        if (value==null || !value.equals(old))
+        {
+            if (old!=null)
+                unbindValue(name,old);
+            if (value!=null)
+                bindValue(name,value);
+
+            _manager.doSessionAttributeListeners(this,name,old,value);
+            
+        }
+    }
+
+    /* ------------------------------------------------------------- */
+    public void setIdChanged(boolean changed)
+    {
+        _idChanged=changed;
+    }
+
+    /* ------------------------------------------------------------- */
+    public void setMaxInactiveInterval(int secs)
+    {
+        _maxIdleMs=(long)secs*1000L;
+    }
+
+    /* ------------------------------------------------------------- */
+    @Override
+    public String toString()
+    {
+        return this.getClass().getName()+":"+getId()+"@"+hashCode();
+    }
+
+    /* ------------------------------------------------------------- */
+    /** If value implements HttpSessionBindingListener, call valueBound() */
+    public void bindValue(java.lang.String name, Object value)
+    {
+        if (value!=null&&value instanceof HttpSessionBindingListener)
+            ((HttpSessionBindingListener)value).valueBound(new HttpSessionBindingEvent(this,name));
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isValid()
+    {
+        return !_invalid;
+    }
+
+    /* ------------------------------------------------------------- */
+    protected void cookieSet()
+    {
+        synchronized (this)
+        {
+            _cookieSet=_accessed;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public int getRequests()
+    {
+        synchronized (this)
+        {
+            return _requests;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setRequests(int requests)
+    {
+        synchronized (this)
+        {
+            _requests=requests;
+        }
+    }
+    
+    /* ------------------------------------------------------------- */
+    /** If value implements HttpSessionBindingListener, call valueUnbound() */
+    public void unbindValue(java.lang.String name, Object value)
+    {
+        if (value!=null&&value instanceof HttpSessionBindingListener)
+            ((HttpSessionBindingListener)value).valueUnbound(new HttpSessionBindingEvent(this,name));
+    }
+
+    /* ------------------------------------------------------------- */
+    public void willPassivate()
+    {
+        synchronized(this)
+        {
+            HttpSessionEvent event = new HttpSessionEvent(this);
+            for (Iterator<Object> iter = _attributes.values().iterator(); iter.hasNext();)
+            {
+                Object value = iter.next();
+                if (value instanceof HttpSessionActivationListener)
+                {
+                    HttpSessionActivationListener listener = (HttpSessionActivationListener) value;
+                    listener.sessionWillPassivate(event);
+                }
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------- */
+    public void didActivate()
+    {
+        synchronized(this)
+        {
+            HttpSessionEvent event = new HttpSessionEvent(this);
+            for (Iterator<Object> iter = _attributes.values().iterator(); iter.hasNext();)
+            {
+                Object value = iter.next();
+                if (value instanceof HttpSessionActivationListener)
+                {
+                    HttpSessionActivationListener listener = (HttpSessionActivationListener) value;
+                    listener.sessionDidActivate(event);
+                }
+            }
+        }
+    }
+    
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/session/AbstractSessionIdManager.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,220 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+import java.security.SecureRandom;
+import java.util.Random;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.eclipse.jetty.server.SessionIdManager;
+import org.eclipse.jetty.util.component.AbstractLifeCycle;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+public abstract class AbstractSessionIdManager extends AbstractLifeCycle implements SessionIdManager
+{
+    private static final Logger LOG = Log.getLogger(AbstractSessionIdManager.class);
+
+    private final static String __NEW_SESSION_ID="org.eclipse.jetty.server.newSessionId";  
+    
+    protected Random _random;
+    protected boolean _weakRandom;
+    protected String _workerName;
+    protected long _reseed=100000L;
+    
+    /* ------------------------------------------------------------ */
+    public AbstractSessionIdManager()
+    {
+    }
+    
+    /* ------------------------------------------------------------ */
+    public AbstractSessionIdManager(Random random)
+    {
+        _random=random;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return the reseed probability
+     */
+    public long getReseed()
+    {
+        return _reseed;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set the reseed probability.
+     * @param reseed  If non zero then when a random long modulo the reseed value == 1, the {@link SecureRandom} will be reseeded.
+     */
+    public void setReseed(long reseed)
+    {
+        _reseed = reseed;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Get the workname. If set, the workername is dot appended to the session
+     * ID and can be used to assist session affinity in a load balancer.
+     * 
+     * @return String or null
+     */
+    public String getWorkerName()
+    {
+        return _workerName;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set the workname. If set, the workername is dot appended to the session
+     * ID and can be used to assist session affinity in a load balancer.
+     * 
+     * @param workerName
+     */
+    public void setWorkerName(String workerName)
+    {
+        if (workerName.contains("."))
+            throw new IllegalArgumentException("Name cannot contain '.'");
+        _workerName=workerName;
+    }
+
+    /* ------------------------------------------------------------ */
+    public Random getRandom()
+    {
+        return _random;
+    }
+
+    /* ------------------------------------------------------------ */
+    public synchronized void setRandom(Random random)
+    {
+        _random=random;
+        _weakRandom=false;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** 
+     * Create a new session id if necessary.
+     * 
+     * @see org.eclipse.jetty.server.SessionIdManager#newSessionId(javax.servlet.http.HttpServletRequest, long)
+     */
+    public String newSessionId(HttpServletRequest request, long created)
+    {
+        synchronized (this)
+        {
+            if (request!=null)
+            {
+                // A requested session ID can only be used if it is in use already.
+                String requested_id=request.getRequestedSessionId();
+                if (requested_id!=null)
+                {
+                    String cluster_id=getClusterId(requested_id);
+                    if (idInUse(cluster_id))
+                        return cluster_id;
+                }
+
+                // Else reuse any new session ID already defined for this request.
+                String new_id=(String)request.getAttribute(__NEW_SESSION_ID);
+                if (new_id!=null&&idInUse(new_id))
+                    return new_id;
+            }
+            
+            // pick a new unique ID!
+            String id=null;
+            while (id==null||id.length()==0||idInUse(id))
+            {
+                long r0=_weakRandom
+                ?(hashCode()^Runtime.getRuntime().freeMemory()^_random.nextInt()^(((long)request.hashCode())<<32))
+                :_random.nextLong();
+                if (r0<0)
+                    r0=-r0;
+
+		// random chance to reseed
+		if (_reseed>0 && (r0%_reseed)== 1L)
+		{
+		    LOG.debug("Reseeding {}",this);
+		    if (_random instanceof SecureRandom)
+		    {
+			SecureRandom secure = (SecureRandom)_random;
+			secure.setSeed(secure.generateSeed(8));
+		    }
+		    else
+		    {
+			_random.setSeed(_random.nextLong()^System.currentTimeMillis()^request.hashCode()^Runtime.getRuntime().freeMemory());
+		    }
+		}
+
+                long r1=_weakRandom
+                ?(hashCode()^Runtime.getRuntime().freeMemory()^_random.nextInt()^(((long)request.hashCode())<<32))
+                :_random.nextLong();
+                if (r1<0)
+                    r1=-r1;
+                id=Long.toString(r0,36)+Long.toString(r1,36);
+                
+                //add in the id of the node to ensure unique id across cluster
+                //NOTE this is different to the node suffix which denotes which node the request was received on
+                if (_workerName!=null)
+                    id=_workerName + id;
+            }
+
+            request.setAttribute(__NEW_SESSION_ID,id);
+            return id;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    protected void doStart() throws Exception
+    {
+       initRandom();
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    protected void doStop() throws Exception
+    {
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Set up a random number generator for the sessionids.
+     * 
+     * By preference, use a SecureRandom but allow to be injected.
+     */
+    public void initRandom ()
+    {
+        if (_random==null)
+        {
+            try
+            {
+                _random=new SecureRandom();
+            }
+            catch (Exception e)
+            {
+                LOG.warn("Could not generate SecureRandom for session-id randomness",e);
+                _random=new Random();
+                _weakRandom=true;
+            }
+        }
+        else
+            _random.setSeed(_random.nextLong()^System.currentTimeMillis()^hashCode()^Runtime.getRuntime().freeMemory()); 
+    }
+    
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/session/AbstractSessionManager.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,1026 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+import static java.lang.Math.round;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.EventListener;
+import java.util.HashSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.SessionCookieConfig;
+import javax.servlet.SessionTrackingMode;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpSessionAttributeListener;
+import javax.servlet.http.HttpSessionBindingEvent;
+import javax.servlet.http.HttpSessionContext;
+import javax.servlet.http.HttpSessionEvent;
+import javax.servlet.http.HttpSessionListener;
+
+import org.eclipse.jetty.http.HttpCookie;
+import org.eclipse.jetty.server.AbstractConnector;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.SessionIdManager;
+import org.eclipse.jetty.server.SessionManager;
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.util.component.AbstractLifeCycle;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.statistic.CounterStatistic;
+import org.eclipse.jetty.util.statistic.SampleStatistic;
+
+/* ------------------------------------------------------------ */
+/**
+ * An Abstract implementation of SessionManager. The partial implementation of
+ * SessionManager interface provides the majority of the handling required to
+ * implement a SessionManager. Concrete implementations of SessionManager based
+ * on AbstractSessionManager need only implement the newSession method to return
+ * a specialised version of the Session inner class that provides an attribute
+ * Map.
+ * <p>
+ */
+@SuppressWarnings("deprecation")
+public abstract class AbstractSessionManager extends AbstractLifeCycle implements SessionManager
+{
+    final static Logger __log = SessionHandler.LOG;
+
+    public Set<SessionTrackingMode> __defaultSessionTrackingModes =
+        Collections.unmodifiableSet(
+            new HashSet<SessionTrackingMode>(
+                    Arrays.asList(new SessionTrackingMode[]{SessionTrackingMode.COOKIE,SessionTrackingMode.URL})));
+        
+    public final static String SESSION_KNOWN_ONLY_TO_AUTHENTICATED="org.eclipse.jetty.security.sessionKnownOnlytoAuthenticated";
+    
+    /* ------------------------------------------------------------ */
+    public final static int __distantFuture=60*60*24*7*52*20;
+
+    static final HttpSessionContext __nullSessionContext=new HttpSessionContext()
+    {
+        public HttpSession getSession(String sessionId)
+        {
+            return null;
+        }
+        
+        @SuppressWarnings({ "rawtypes", "unchecked" })
+        public Enumeration getIds()
+        {
+            return Collections.enumeration(Collections.EMPTY_LIST);
+        }
+    };
+    
+    private boolean _usingCookies=true;
+
+    /* ------------------------------------------------------------ */
+    // Setting of max inactive interval for new sessions
+    // -1 means no timeout
+    protected int _dftMaxIdleSecs=-1;
+    protected SessionHandler _sessionHandler;
+    protected boolean _httpOnly=false;
+    protected SessionIdManager _sessionIdManager;
+    protected boolean _secureCookies=false;
+    protected boolean _secureRequestOnly=true;
+
+    protected final List<HttpSessionAttributeListener> _sessionAttributeListeners = new CopyOnWriteArrayList<HttpSessionAttributeListener>();
+    protected final List<HttpSessionListener> _sessionListeners= new CopyOnWriteArrayList<HttpSessionListener>();
+
+    protected ClassLoader _loader;
+    protected ContextHandler.Context _context;
+    protected String _sessionCookie=__DefaultSessionCookie;
+    protected String _sessionIdPathParameterName = __DefaultSessionIdPathParameterName;
+    protected String _sessionIdPathParameterNamePrefix =";"+ _sessionIdPathParameterName +"=";
+    protected String _sessionDomain;
+    protected String _sessionPath;
+    protected int _maxCookieAge=-1;
+    protected int _refreshCookieAge;
+    protected boolean _nodeIdInSessionId;
+    protected boolean _checkingRemoteSessionIdEncoding;
+    protected String _sessionComment;
+
+    public Set<SessionTrackingMode> _sessionTrackingModes;
+
+    private boolean _usingURLs;
+    
+    protected final CounterStatistic _sessionsStats = new CounterStatistic();
+    protected final SampleStatistic _sessionTimeStats = new SampleStatistic();
+    
+    
+    /* ------------------------------------------------------------ */
+    public static HttpSession renewSession (HttpServletRequest request, HttpSession httpSession, boolean authenticated)
+    {
+        Map<String,Object> attributes = new HashMap<String, Object>();
+
+        for (Enumeration<String> e=httpSession.getAttributeNames();e.hasMoreElements();)
+        {
+            String name=e.nextElement();
+            attributes.put(name,httpSession.getAttribute(name));
+            httpSession.removeAttribute(name);
+        }
+
+        httpSession.invalidate();       
+        httpSession = request.getSession(true);
+        if (authenticated)
+            httpSession.setAttribute(SESSION_KNOWN_ONLY_TO_AUTHENTICATED, Boolean.TRUE);
+        for (Map.Entry<String, Object> entry: attributes.entrySet())
+            httpSession.setAttribute(entry.getKey(),entry.getValue());
+        return httpSession;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public AbstractSessionManager()
+    {
+        setSessionTrackingModes(__defaultSessionTrackingModes);
+    }
+
+    /* ------------------------------------------------------------ */
+    public ContextHandler.Context getContext()
+    {
+        return _context;
+    }
+
+    /* ------------------------------------------------------------ */
+    public ContextHandler getContextHandler()
+    {
+        return _context.getContextHandler();
+    }
+    
+    public String getSessionPath()
+    {
+        return _sessionPath;
+    }
+
+    public int getMaxCookieAge()
+    {
+        return _maxCookieAge;
+    }
+
+    /* ------------------------------------------------------------ */
+    public HttpCookie access(HttpSession session,boolean secure)
+    {
+        long now=System.currentTimeMillis();
+
+        AbstractSession s = ((SessionIf)session).getSession();
+
+       if (s.access(now))
+       {
+            // Do we need to refresh the cookie?
+            if (isUsingCookies() &&
+                (s.isIdChanged() ||
+                (getSessionCookieConfig().getMaxAge()>0 && getRefreshCookieAge()>0 && ((now-s.getCookieSetTime())/1000>getRefreshCookieAge()))
+                )
+               )
+            {
+                HttpCookie cookie=getSessionCookie(session,_context==null?"/":(_context.getContextPath()),secure);
+                s.cookieSet();
+                s.setIdChanged(false);
+                return cookie;
+            }
+        }
+        return null;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void addEventListener(EventListener listener)
+    {
+        if (listener instanceof HttpSessionAttributeListener)
+            _sessionAttributeListeners.add((HttpSessionAttributeListener)listener);
+        if (listener instanceof HttpSessionListener)
+            _sessionListeners.add((HttpSessionListener)listener);
+    }
+
+    /* ------------------------------------------------------------ */
+    public void clearEventListeners()
+    {
+        _sessionAttributeListeners.clear();
+        _sessionListeners.clear();
+    }
+
+    /* ------------------------------------------------------------ */
+    public void complete(HttpSession session)
+    {
+        AbstractSession s = ((SessionIf)session).getSession();
+        s.complete();
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void doStart() throws Exception
+    {
+        _context=ContextHandler.getCurrentContext();
+        _loader=Thread.currentThread().getContextClassLoader();
+
+        if (_sessionIdManager==null)
+        {
+            final Server server=getSessionHandler().getServer();
+            synchronized (server)
+            {
+                _sessionIdManager=server.getSessionIdManager();
+                if (_sessionIdManager==null)
+                {
+                    _sessionIdManager=new HashSessionIdManager();
+                    server.setSessionIdManager(_sessionIdManager);
+                }
+            }
+        }
+        if (!_sessionIdManager.isStarted())
+            _sessionIdManager.start();
+
+        // Look for a session cookie name
+        if (_context!=null)
+        {
+            String tmp=_context.getInitParameter(SessionManager.__SessionCookieProperty);
+            if (tmp!=null)
+                _sessionCookie=tmp;
+
+            tmp=_context.getInitParameter(SessionManager.__SessionIdPathParameterNameProperty);
+            if (tmp!=null)
+                setSessionIdPathParameterName(tmp);
+
+            // set up the max session cookie age if it isn't already
+            if (_maxCookieAge==-1)
+            {
+                tmp=_context.getInitParameter(SessionManager.__MaxAgeProperty);
+                if (tmp!=null)
+                    _maxCookieAge=Integer.parseInt(tmp.trim());
+            }
+
+            // set up the session domain if it isn't already
+            if (_sessionDomain==null)
+                _sessionDomain=_context.getInitParameter(SessionManager.__SessionDomainProperty);
+
+            // set up the sessionPath if it isn't already
+            if (_sessionPath==null)
+                _sessionPath=_context.getInitParameter(SessionManager.__SessionPathProperty);
+            
+            tmp=_context.getInitParameter(SessionManager.__CheckRemoteSessionEncoding);
+            if (tmp!=null)
+                _checkingRemoteSessionIdEncoding=Boolean.parseBoolean(tmp);
+        }
+
+        super.doStart();
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void doStop() throws Exception
+    {
+        super.doStop();
+
+        invalidateSessions();
+
+        _loader=null;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the httpOnly.
+     */
+    public boolean getHttpOnly()
+    {
+        return _httpOnly;
+    }
+
+    /* ------------------------------------------------------------ */
+    public HttpSession getHttpSession(String nodeId)
+    {
+        String cluster_id = getSessionIdManager().getClusterId(nodeId);
+
+        AbstractSession session = getSession(cluster_id);
+        if (session!=null && !session.getNodeId().equals(nodeId))
+            session.setIdChanged(true);
+        return session;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @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
+     */
+    public SessionIdManager getSessionIdManager()
+    {
+        return _sessionIdManager;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return seconds
+     */
+    @Override
+    public int getMaxInactiveInterval()
+    {
+        return _dftMaxIdleSecs;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @see #getSessionsMax()
+     */
+    @Deprecated
+    public int getMaxSessions()
+    {
+        return getSessionsMax();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return maximum number of sessions
+     */
+    public int getSessionsMax()
+    {
+        return (int)_sessionsStats.getMax();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return total number of sessions
+     */
+    public int getSessionsTotal()
+    {
+        return (int)_sessionsStats.getTotal();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @deprecated use {@link #getSessionIdManager()}
+     */
+    @Deprecated
+    public SessionIdManager getMetaManager()
+    {
+        return getSessionIdManager();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @deprecated always returns 0. no replacement available.
+     */
+    @Deprecated
+    public int getMinSessions()
+    {
+        return 0;
+    }
+
+    /* ------------------------------------------------------------ */
+    public int getRefreshCookieAge()
+    {
+        return _refreshCookieAge;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return same as SessionCookieConfig.getSecure(). If true, session
+     * cookies are ALWAYS marked as secure. If false, a session cookie is
+     * ONLY marked as secure if _secureRequestOnly == true and it is a HTTPS request.
+     */
+    public boolean getSecureCookies()
+    {
+        return _secureCookies;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @return true if session cookie is to be marked as secure only on HTTPS requests
+     */
+    public boolean isSecureRequestOnly()
+    {
+        return _secureRequestOnly;
+    }
+    
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @return if true, session cookie will be marked as secure only iff 
+     * HTTPS request. Can be overridden by setting SessionCookieConfig.setSecure(true),
+     * in which case the session cookie will be marked as secure on both HTTPS and HTTP.
+     */
+    public void setSecureRequestOnly(boolean secureRequestOnly)
+    {
+        _secureRequestOnly = secureRequestOnly;
+    }
+
+    
+    
+    /* ------------------------------------------------------------ */
+    public String getSessionCookie()
+    {
+        return _sessionCookie;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** 
+     * A sessioncookie is marked as secure IFF any of the following conditions are true:
+     * <ol>
+     * <li>SessionCookieConfig.setSecure == true</li>
+     * <li>SessionCookieConfig.setSecure == false && _secureRequestOnly==true && request is HTTPS</li>
+     * </ol>
+     * According to SessionCookieConfig javadoc, case 1 can be used when:
+     * "... even though the request that initiated the session came over HTTP, 
+     * is to support a topology where the web container is front-ended by an 
+     * SSL offloading load balancer. In this case, the traffic between the client 
+     * and the load balancer will be over HTTPS, whereas the traffic between the 
+     * load balancer and the web container will be over HTTP."
+     * 
+     * For case 2, you can use _secureRequestOnly to determine if you want the
+     * Servlet Spec 3.0  default behaviour when SessionCookieConfig.setSecure==false, 
+     * which is:
+     * "they shall be marked as secure only if the request that initiated the 
+     * corresponding session was also secure"
+     * 
+     * The default for _secureRequestOnly is true, which gives the above behaviour. If
+     * you set it to false, then a session cookie is NEVER marked as secure, even if
+     * the initiating request was secure.
+     * 
+     * @see org.eclipse.jetty.server.SessionManager#getSessionCookie(javax.servlet.http.HttpSession, java.lang.String, boolean)
+     */
+    public HttpCookie getSessionCookie(HttpSession session, String contextPath, boolean requestIsSecure)
+    {
+        if (isUsingCookies())
+        {
+            String sessionPath = (_sessionPath==null) ? contextPath : _sessionPath;
+            sessionPath = (sessionPath==null||sessionPath.length()==0) ? "/" : sessionPath;
+            String id = getNodeId(session);
+            HttpCookie cookie = null;
+            if (_sessionComment == null)
+            {
+                cookie = new HttpCookie(
+                                        _sessionCookie,
+                                        id,
+                                        _sessionDomain,
+                                        sessionPath,
+                                        _cookieConfig.getMaxAge(),
+                                        _cookieConfig.isHttpOnly(),
+                                        _cookieConfig.isSecure() || (isSecureRequestOnly() && requestIsSecure));                  
+            }
+            else
+            {
+                cookie = new HttpCookie(
+                                        _sessionCookie,
+                                        id,
+                                        _sessionDomain,
+                                        sessionPath,
+                                        _cookieConfig.getMaxAge(),
+                                        _cookieConfig.isHttpOnly(),
+                                        _cookieConfig.isSecure() || (isSecureRequestOnly() && requestIsSecure),
+                                        _sessionComment,
+                                        1);    
+            }
+
+            return cookie;
+        }
+        return null;
+    }
+
+    public String getSessionDomain()
+    {
+        return _sessionDomain;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the sessionHandler.
+     */
+    public SessionHandler getSessionHandler()
+    {
+        return _sessionHandler;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @deprecated  Need to review if it is needed.
+     */
+    @SuppressWarnings("rawtypes")
+    public Map getSessionMap()
+    {
+        throw new UnsupportedOperationException();
+    }
+
+   
+
+    /* ------------------------------------------------------------ */
+    public int getSessions()
+    {
+        return (int)_sessionsStats.getCurrent();
+    }
+
+    /* ------------------------------------------------------------ */
+    public String getSessionIdPathParameterName()
+    {
+        return _sessionIdPathParameterName;
+    }
+
+    /* ------------------------------------------------------------ */
+    public String getSessionIdPathParameterNamePrefix()
+    {
+        return _sessionIdPathParameterNamePrefix;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the usingCookies.
+     */
+    public boolean isUsingCookies()
+    {
+        return _usingCookies;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isValid(HttpSession session)
+    {
+        AbstractSession s = ((SessionIf)session).getSession();
+        return s.isValid();
+    }
+
+    /* ------------------------------------------------------------ */
+    public String getClusterId(HttpSession session)
+    {
+        AbstractSession s = ((SessionIf)session).getSession();
+        return s.getClusterId();
+    }
+
+    /* ------------------------------------------------------------ */
+    public String getNodeId(HttpSession session)
+    {
+        AbstractSession s = ((SessionIf)session).getSession();
+        return s.getNodeId();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Create a new HttpSession for a request
+     */
+    public HttpSession newHttpSession(HttpServletRequest request)
+    {
+        AbstractSession session=newSession(request);
+        session.setMaxInactiveInterval(_dftMaxIdleSecs);
+        addSession(session,true);
+        return session;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void removeEventListener(EventListener listener)
+    {
+        if (listener instanceof HttpSessionAttributeListener)
+            _sessionAttributeListeners.remove(listener);
+        if (listener instanceof HttpSessionListener)
+            _sessionListeners.remove(listener);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @see #statsReset()
+     */
+    @Deprecated
+    public void resetStats()
+    {
+        statsReset();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Reset statistics values
+     */
+    public void statsReset()
+    {
+        _sessionsStats.reset(getSessions());
+        _sessionTimeStats.reset();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param httpOnly
+     *            The httpOnly to set.
+     */
+    public void setHttpOnly(boolean httpOnly)
+    {
+        _httpOnly=httpOnly;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @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.
+     */
+    public void setSessionIdManager(SessionIdManager metaManager)
+    {
+        _sessionIdManager=metaManager;
+    }
+
+
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param seconds
+     */
+    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.
+     */
+    public void setSessionHandler(SessionHandler sessionHandler)
+    {
+        _sessionHandler=sessionHandler;
+    }
+
+ 
+    /* ------------------------------------------------------------ */
+    public void setSessionIdPathParameterName(String param)
+    {
+        _sessionIdPathParameterName =(param==null||"none".equals(param))?null:param;
+        _sessionIdPathParameterNamePrefix =(param==null||"none".equals(param))?null:(";"+ _sessionIdPathParameterName +"=");
+    }
+    /* ------------------------------------------------------------ */
+    /**
+     * @param usingCookies
+     *            The usingCookies to set.
+     */
+    public void setUsingCookies(boolean usingCookies)
+    {
+        _usingCookies=usingCookies;
+    }
+
+
+    protected abstract void addSession(AbstractSession session);
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Add the session Registers the session with this manager and registers the
+     * session ID with the sessionIDManager;
+     */
+    protected void addSession(AbstractSession session, boolean created)
+    {
+        synchronized (_sessionIdManager)
+        {
+            _sessionIdManager.addSession(session);
+            addSession(session);
+        }
+
+        if (created)
+        {
+            _sessionsStats.increment();
+            if (_sessionListeners!=null)
+            {
+                HttpSessionEvent event=new HttpSessionEvent(session);
+                for (HttpSessionListener listener : _sessionListeners)
+                    listener.sessionCreated(event);
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Get a known existing session
+     * @param idInCluster The session ID in the cluster, stripped of any worker name.
+     * @return A Session or null if none exists.
+     */
+    public abstract AbstractSession getSession(String idInCluster);
+
+    protected abstract void invalidateSessions() throws Exception;
+
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Create a new session instance
+     * @param request
+     * @return the new session
+     */
+    protected abstract AbstractSession newSession(HttpServletRequest request);
+
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return true if the cluster node id (worker id) is returned as part of the session id by {@link HttpSession#getId()}. Default is false.
+     */
+    public boolean isNodeIdInSessionId()
+    {
+        return _nodeIdInSessionId;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param nodeIdInSessionId true if the cluster node id (worker id) will be returned as part of the session id by {@link HttpSession#getId()}. Default is false.
+     */
+    public void setNodeIdInSessionId(boolean nodeIdInSessionId)
+    {
+        _nodeIdInSessionId=nodeIdInSessionId;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Remove session from manager
+     * @param session The session to remove
+     * @param invalidate True if {@link HttpSessionListener#sessionDestroyed(HttpSessionEvent)} and
+     * {@link SessionIdManager#invalidateAll(String)} should be called.
+     */
+    public void removeSession(HttpSession session, boolean invalidate)
+    {
+        AbstractSession s = ((SessionIf)session).getSession();
+        removeSession(s,invalidate);
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Remove session from manager
+     * @param session The session to remove
+     * @param invalidate True if {@link HttpSessionListener#sessionDestroyed(HttpSessionEvent)} and
+     * {@link SessionIdManager#invalidateAll(String)} should be called.
+     */
+    public void removeSession(AbstractSession session, boolean invalidate)
+    {
+        // Remove session from context and global maps
+        boolean removed = removeSession(session.getClusterId());
+        
+        if (removed)
+        {
+            _sessionsStats.decrement();
+            _sessionTimeStats.set(round((System.currentTimeMillis() - session.getCreationTime())/1000.0));
+            
+            // Remove session from all context and global id maps
+            _sessionIdManager.removeSession(session);
+            if (invalidate)
+                _sessionIdManager.invalidateAll(session.getClusterId());
+            
+            if (invalidate && _sessionListeners!=null)
+            {
+                HttpSessionEvent event=new HttpSessionEvent(session);
+                for (HttpSessionListener listener : _sessionListeners)
+                    listener.sessionDestroyed(event);
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    protected abstract boolean removeSession(String idInCluster);
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @return maximum amount of time session remained valid
+     */
+    public long getSessionTimeMax()
+    {
+        return _sessionTimeStats.getMax();
+    }
+
+    /* ------------------------------------------------------------ */
+    public Set<SessionTrackingMode> getDefaultSessionTrackingModes()
+    {
+        return __defaultSessionTrackingModes;
+    }
+
+    /* ------------------------------------------------------------ */
+    public Set<SessionTrackingMode> getEffectiveSessionTrackingModes()
+    {
+        return Collections.unmodifiableSet(_sessionTrackingModes);
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void setSessionTrackingModes(Set<SessionTrackingMode> sessionTrackingModes)
+    {
+        _sessionTrackingModes=new HashSet<SessionTrackingMode>(sessionTrackingModes);
+        _usingCookies=_sessionTrackingModes.contains(SessionTrackingMode.COOKIE);
+        _usingURLs=_sessionTrackingModes.contains(SessionTrackingMode.URL);
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public boolean isUsingURLs()
+    {
+        return _usingURLs;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    public SessionCookieConfig getSessionCookieConfig()
+    {
+        return _cookieConfig;
+    } 
+
+    /* ------------------------------------------------------------ */
+    private SessionCookieConfig _cookieConfig =
+        new SessionCookieConfig()
+        {
+            @Override
+            public String getComment()
+            {
+                return _sessionComment;
+            }
+
+            @Override
+            public String getDomain()
+            {
+                return _sessionDomain;
+            }
+
+            @Override
+            public int getMaxAge()
+            {
+                return _maxCookieAge;
+            }
+
+            @Override
+            public String getName()
+            {
+                return _sessionCookie;
+            }
+
+            @Override
+            public String getPath()
+            {
+                return _sessionPath;
+            }
+
+            @Override
+            public boolean isHttpOnly()
+            {
+                return _httpOnly;
+            }
+
+            @Override
+            public boolean isSecure()
+            {
+                return _secureCookies;
+            }
+
+            @Override
+            public void setComment(String comment)
+            {
+                _sessionComment = comment; 
+            }
+
+            @Override
+            public void setDomain(String domain)
+            {
+                _sessionDomain=domain;
+            }
+
+            @Override
+            public void setHttpOnly(boolean httpOnly)
+            {
+                _httpOnly=httpOnly;
+            }
+
+            @Override
+            public void setMaxAge(int maxAge)
+            {
+                _maxCookieAge=maxAge;
+            }
+
+            @Override
+            public void setName(String name)
+            {
+                _sessionCookie=name;
+            }
+
+            @Override
+            public void setPath(String path)
+            {
+                _sessionPath=path;
+            }
+
+            @Override
+            public void setSecure(boolean secure)
+            {
+                _secureCookies=secure;
+            }
+        
+        };
+
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return total amount of time all sessions remained valid
+     */
+    public long getSessionTimeTotal()
+    {
+        return _sessionTimeStats.getTotal();
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @return mean amount of time session remained valid
+     */
+    public double getSessionTimeMean()
+    {
+        return _sessionTimeStats.getMean();
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @return standard deviation of amount of time session remained valid
+     */
+    public double getSessionTimeStdDev()
+    {
+        return _sessionTimeStats.getStdDev();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.SessionManager#isCheckingRemoteSessionIdEncoding()
+     */
+    public boolean isCheckingRemoteSessionIdEncoding()
+    {
+        return _checkingRemoteSessionIdEncoding;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.SessionManager#setCheckingRemoteSessionIdEncoding(boolean)
+     */
+    public void setCheckingRemoteSessionIdEncoding(boolean remote)
+    {
+        _checkingRemoteSessionIdEncoding=remote;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    /**
+     * Interface that any session wrapper should implement so that
+     * SessionManager may access the Jetty session implementation.
+     *
+     */
+    public interface SessionIf extends HttpSession
+    {
+        public AbstractSession getSession();
+    }
+
+    public void doSessionAttributeListeners(AbstractSession session, String name, Object old, Object value)
+    {
+        if (!_sessionAttributeListeners.isEmpty())
+        {
+            HttpSessionBindingEvent event=new HttpSessionBindingEvent(session,name,old==null?value:old);
+
+            for (HttpSessionAttributeListener l : _sessionAttributeListeners)
+            {
+                if (old==null)
+                    l.attributeAdded(event);
+                else if (value==null)
+                    l.attributeRemoved(event);
+                else
+                    l.attributeReplaced(event);
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/session/HashSessionIdManager.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,222 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+import org.eclipse.jetty.server.SessionIdManager;
+
+/* ------------------------------------------------------------ */
+/**
+ * HashSessionIdManager. An in-memory implementation of the session ID manager.
+ */
+public class HashSessionIdManager extends AbstractSessionIdManager
+{
+    private final Map<String, Set<WeakReference<HttpSession>>> _sessions = new HashMap<String, Set<WeakReference<HttpSession>>>();
+
+    /* ------------------------------------------------------------ */
+    public HashSessionIdManager()
+    {
+    }
+
+    /* ------------------------------------------------------------ */
+    public HashSessionIdManager(Random random)
+    {
+        super(random);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Collection of String session IDs
+     */
+    public Collection<String> getSessions()
+    {
+        return Collections.unmodifiableCollection(_sessions.keySet());
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Collection of Sessions for the passed session ID
+     */
+    public Collection<HttpSession> getSession(String id)
+    {
+        ArrayList<HttpSession> sessions = new ArrayList<HttpSession>();
+        Set<WeakReference<HttpSession>> refs =_sessions.get(id);
+        if (refs!=null)
+        {
+            for (WeakReference<HttpSession> ref: refs)
+            {
+                HttpSession session = ref.get();
+                if (session!=null)
+                    sessions.add(session);
+            }
+        }
+        return sessions;
+    }
+    /* ------------------------------------------------------------ */
+    /** Get the session ID with any worker ID.
+     * 
+     * @param clusterId
+     * @param request
+     * @return sessionId plus any worker ID.
+     */
+    public String getNodeId(String clusterId,HttpServletRequest request) 
+    {
+        // used in Ajp13Parser
+        String worker=request==null?null:(String)request.getAttribute("org.eclipse.jetty.ajp.JVMRoute");
+        if (worker!=null) 
+            return clusterId+'.'+worker; 
+        
+        if (_workerName!=null) 
+            return clusterId+'.'+_workerName;
+       
+        return clusterId;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Get the session ID without any worker ID.
+     * 
+     * @param nodeId the node id
+     * @return sessionId without any worker ID.
+     */
+    public String getClusterId(String nodeId) 
+    {
+        int dot=nodeId.lastIndexOf('.');
+        return (dot>0)?nodeId.substring(0,dot):nodeId;
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    protected void doStart() throws Exception
+    {        
+        super.doStart();
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    protected void doStop() throws Exception
+    {
+        _sessions.clear(); 
+        super.doStop();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see SessionIdManager#idInUse(String)
+     */
+    public boolean idInUse(String id)
+    {
+        synchronized (this)
+        {
+            return _sessions.containsKey(id);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see SessionIdManager#addSession(HttpSession)
+     */
+    public void addSession(HttpSession session)
+    {
+        String id = getClusterId(session.getId());
+        WeakReference<HttpSession> ref = new WeakReference<HttpSession>(session);
+        
+        synchronized (this)
+        {
+            Set<WeakReference<HttpSession>> sessions = _sessions.get(id);
+            if (sessions==null)
+            {
+                sessions=new HashSet<WeakReference<HttpSession>>();
+                _sessions.put(id,sessions);
+            }
+            sessions.add(ref);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see SessionIdManager#removeSession(HttpSession)
+     */
+    public void removeSession(HttpSession session)
+    {
+        String id = getClusterId(session.getId());
+        
+        synchronized (this)
+        {
+            Collection<WeakReference<HttpSession>> sessions = _sessions.get(id);
+            if (sessions!=null)
+            {
+                for (Iterator<WeakReference<HttpSession>> iter = sessions.iterator(); iter.hasNext();)
+                {
+                    WeakReference<HttpSession> ref = iter.next();
+                    HttpSession s=ref.get();
+                    if (s==null)
+                    {
+                        iter.remove();
+                        continue;
+                    }
+                    if (s==session)
+                    {
+                        iter.remove();
+                        break;
+                    }
+                }
+                if (sessions.isEmpty())
+                    _sessions.remove(id);
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see SessionIdManager#invalidateAll(String)
+     */
+    public void invalidateAll(String id)
+    {
+        Collection<WeakReference<HttpSession>> sessions;
+        synchronized (this)
+        {
+            sessions = _sessions.remove(id);
+        }
+        
+        if (sessions!=null)
+        {
+            for (WeakReference<HttpSession> ref: sessions)
+            {
+                AbstractSession session=(AbstractSession)ref.get();
+                if (session!=null && session.isValid())
+                    session.invalidate();
+            }
+            sessions.clear();
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/session/HashSessionManager.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,640 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.log.Logger;
+
+
+/* ------------------------------------------------------------ */
+/** 
+ * HashSessionManager
+ * 
+ * An in-memory implementation of SessionManager.
+ * <p>
+ * This manager supports saving sessions to disk, either periodically or at shutdown.
+ * Sessions can also have their content idle saved to disk to reduce the memory overheads of large idle sessions.
+ * <p>
+ * This manager will create it's own Timer instance to scavenge threads, unless it discovers a shared Timer instance
+ * set as the "org.eclipse.jetty.server.session.timer" attribute of the ContextHandler.
+ *
+ */
+public class HashSessionManager extends AbstractSessionManager
+{
+    final static Logger __log = SessionHandler.LOG;
+
+    protected final ConcurrentMap<String,HashedSession> _sessions=new ConcurrentHashMap<String,HashedSession>();
+    private static int __id;
+    private Timer _timer;
+    private boolean _timerStop=false;
+    private TimerTask _task;
+    long _scavengePeriodMs=30000;
+    long _savePeriodMs=0; //don't do period saves by default
+    long _idleSavePeriodMs = 0; // don't idle save sessions by default.
+    private TimerTask _saveTask;
+    File _storeDir;
+    private boolean _lazyLoad=false;
+    private volatile boolean _sessionsLoaded=false;
+    private boolean _deleteUnrestorableSessions=false;
+    
+
+
+
+    /* ------------------------------------------------------------ */
+    public HashSessionManager()
+    {
+        super();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.servlet.AbstractSessionManager#doStart()
+     */
+    @Override
+    public void doStart() throws Exception
+    {
+        super.doStart();
+
+        _timerStop=false;
+        ServletContext context = ContextHandler.getCurrentContext();
+        if (context!=null)
+            _timer=(Timer)context.getAttribute("org.eclipse.jetty.server.session.timer");
+        if (_timer==null)
+        {
+            _timerStop=true;
+            _timer=new Timer("HashSessionScavenger-"+__id++, true);
+        }
+
+        setScavengePeriod(getScavengePeriod());
+
+        if (_storeDir!=null)
+        {
+            if (!_storeDir.exists())
+                _storeDir.mkdirs();
+
+            if (!_lazyLoad)
+                restoreSessions();
+        }
+
+        setSavePeriod(getSavePeriod());
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.servlet.AbstractSessionManager#doStop()
+     */
+    @Override
+    public void doStop() throws Exception
+    {
+        // stop the scavengers
+        synchronized(this)
+        {
+            if (_saveTask!=null)
+                _saveTask.cancel();
+            _saveTask=null;
+            if (_task!=null)
+                _task.cancel();
+            _task=null;
+            if (_timer!=null && _timerStop)
+                _timer.cancel();
+            _timer=null;
+        }
+
+        // This will callback invalidate sessions - where we decide if we will save
+        super.doStop();
+
+        _sessions.clear();
+
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return the period in seconds at which a check is made for sessions to be invalidated.
+     */
+    public int getScavengePeriod()
+    {
+        return (int)(_scavengePeriodMs/1000);
+    }
+
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public int getSessions()
+    {
+        int sessions=super.getSessions();
+        if (__log.isDebugEnabled())
+        {
+            if (_sessions.size()!=sessions)
+                __log.warn("sessions: "+_sessions.size()+"!="+sessions);
+        }
+        return sessions;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return seconds Idle period after which a session is saved
+     */
+    public int getIdleSavePeriod()
+    {
+      if (_idleSavePeriodMs <= 0)
+        return 0;
+
+      return (int)(_idleSavePeriodMs / 1000);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Configures the period in seconds after which a session is deemed idle and saved
+     * to save on session memory.
+     *
+     * The session is persisted, the values attribute map is cleared and the session set to idled.
+     *
+     * @param seconds Idle period after which a session is saved
+     */
+    public void setIdleSavePeriod(int seconds)
+    {
+      _idleSavePeriodMs = seconds * 1000L;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void setMaxInactiveInterval(int seconds)
+    {
+        super.setMaxInactiveInterval(seconds);
+        if (_dftMaxIdleSecs>0&&_scavengePeriodMs>_dftMaxIdleSecs*1000L)
+            setScavengePeriod((_dftMaxIdleSecs+9)/10);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param seconds the period is seconds at which sessions are periodically saved to disk
+     */
+    public void setSavePeriod (int seconds)
+    {
+        long period = (seconds * 1000L);
+        if (period < 0)
+            period=0;
+        _savePeriodMs=period;
+
+        if (_timer!=null)
+        {
+            synchronized (this)
+            {
+                if (_saveTask!=null)
+                    _saveTask.cancel();
+                if (_savePeriodMs > 0 && _storeDir!=null) //only save if we have a directory configured
+                {
+                    _saveTask = new TimerTask()
+                    {
+                        @Override
+                        public void run()
+                        {
+                            try
+                            {
+                                saveSessions(true);
+                            }
+                            catch (Exception e)
+                            {
+                                __log.warn(e);
+                            }
+                        }
+                    };
+                    _timer.schedule(_saveTask,_savePeriodMs,_savePeriodMs);
+                }
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return the period in seconds at which sessions are periodically saved to disk
+     */
+    public int getSavePeriod ()
+    {
+        if (_savePeriodMs<=0)
+            return 0;
+
+        return (int)(_savePeriodMs/1000);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param seconds the period in seconds at which a check is made for sessions to be invalidated.
+     */
+    public void setScavengePeriod(int seconds)
+    { 
+        if (seconds==0)
+            seconds=60;
+
+        long old_period=_scavengePeriodMs;
+        long period=seconds*1000L;
+        if (period>60000)
+            period=60000;
+        if (period<1000)
+            period=1000;
+
+        _scavengePeriodMs=period;
+    
+        if (_timer!=null && (period!=old_period || _task==null))
+        {
+            synchronized (this)
+            {
+                if (_task!=null)
+                    _task.cancel();
+                _task = new TimerTask()
+                {
+                    @Override
+                    public void run()
+                    {
+                        scavenge();
+                    }
+                };
+                _timer.schedule(_task,_scavengePeriodMs,_scavengePeriodMs);
+            }
+        }
+    }
+
+    /* -------------------------------------------------------------- */
+    /**
+     * Find sessions that have timed out and invalidate them. This runs in the
+     * SessionScavenger thread.
+     */
+    protected void scavenge()
+    {
+        //don't attempt to scavenge if we are shutting down
+        if (isStopping() || isStopped())
+            return;
+
+        Thread thread=Thread.currentThread();
+        ClassLoader old_loader=thread.getContextClassLoader();
+        try
+        {
+            if (_loader!=null)
+                thread.setContextClassLoader(_loader);
+
+            // For each session
+            long now=System.currentTimeMillis();
+          
+            for (Iterator<HashedSession> i=_sessions.values().iterator(); i.hasNext();)
+            {
+                HashedSession session=i.next();
+                long idleTime=session.getMaxInactiveInterval()*1000L; 
+                if (idleTime>0&&session.getAccessed()+idleTime<now)
+                {
+                    // Found a stale session, add it to the list
+                    try
+                    {
+                        session.timeout();
+                    }
+                    catch (Exception e)
+                    {
+                        __log.warn("Problem scavenging sessions", e);
+                    }
+                }
+                else if (_idleSavePeriodMs > 0 && session.getAccessed()+_idleSavePeriodMs < now)
+                {
+                    try
+                    {
+                        session.idle();
+                    }
+                    catch (Exception e)
+                    {
+                        __log.warn("Problem idling session "+ session.getId(), e);
+                    }
+                }
+            }
+        }       
+        finally
+        {
+            thread.setContextClassLoader(old_loader);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    protected void addSession(AbstractSession session)
+    {
+        if (isRunning())
+            _sessions.put(session.getClusterId(),(HashedSession)session);
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public AbstractSession getSession(String idInCluster)
+    {
+        if ( _lazyLoad && !_sessionsLoaded)
+        {
+            try
+            {
+                restoreSessions();
+            }
+            catch(Exception e)
+            {
+                __log.warn(e);
+            }
+        }
+
+        Map<String,HashedSession> sessions=_sessions;
+        if (sessions==null)
+            return null;
+
+        HashedSession session = sessions.get(idInCluster);
+
+        if (session == null && _lazyLoad)
+            session=restoreSession(idInCluster);
+        if (session == null)
+            return null;
+
+        if (_idleSavePeriodMs!=0)
+            session.deIdle();
+
+        return session;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    protected void invalidateSessions() throws Exception
+    {
+        // Invalidate all sessions to cause unbind events
+        ArrayList<HashedSession> sessions=new ArrayList<HashedSession>(_sessions.values());
+        int loop=100;
+        while (sessions.size()>0 && loop-->0)
+        {
+            // If we are called from doStop
+            if (isStopping() && _storeDir != null && _storeDir.exists() && _storeDir.canWrite())
+            {
+                // Then we only save and remove the session - it is not invalidated.
+                for (HashedSession session : sessions)
+                {
+                    session.save(false);
+                    removeSession(session,false);
+                }
+            }
+            else
+            {
+                for (HashedSession session : sessions)
+                    session.invalidate();
+            }
+
+            // check that no new sessions were created while we were iterating
+            sessions=new ArrayList<HashedSession>(_sessions.values());
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    protected AbstractSession newSession(HttpServletRequest request)
+    {
+        return new HashedSession(this, request);
+    }
+
+    /* ------------------------------------------------------------ */
+    protected AbstractSession newSession(long created, long accessed, String clusterId)
+    {
+        return new HashedSession(this, created,accessed, clusterId);
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    protected boolean removeSession(String clusterId)
+    {
+        return _sessions.remove(clusterId)!=null;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setStoreDirectory (File dir) throws IOException
+    { 
+        // CanonicalFile is used to capture the base store directory in a way that will
+        // work on Windows.  Case differences may through off later checks using this directory.
+        _storeDir=dir.getCanonicalFile();
+    }
+
+    /* ------------------------------------------------------------ */
+    public File getStoreDirectory ()
+    {
+        return _storeDir;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setLazyLoad(boolean lazyLoad)
+    {
+        _lazyLoad = lazyLoad;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isLazyLoad()
+    {
+        return _lazyLoad;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public boolean isDeleteUnrestorableSessions()
+    {
+        return _deleteUnrestorableSessions;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public void setDeleteUnrestorableSessions(boolean deleteUnrestorableSessions)
+    {
+        _deleteUnrestorableSessions = deleteUnrestorableSessions;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void restoreSessions () throws Exception
+    {
+        _sessionsLoaded = true;
+
+        if (_storeDir==null || !_storeDir.exists())
+        {
+            return;
+        }
+
+        if (!_storeDir.canRead())
+        {
+            __log.warn ("Unable to restore Sessions: Cannot read from Session storage directory "+_storeDir.getAbsolutePath());
+            return;
+        }
+
+        String[] files = _storeDir.list();
+        for (int i=0;files!=null&&i<files.length;i++)
+        {
+            restoreSession(files[i]);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    protected synchronized HashedSession restoreSession(String idInCuster)
+    {        
+        File file = new File(_storeDir,idInCuster);
+
+        FileInputStream in = null;
+        Exception error = null;
+        try
+        {
+            if (file.exists())
+            {
+                in = new FileInputStream(file);
+                HashedSession session = restoreSession(in, null);
+                addSession(session, false);
+                session.didActivate();
+                return session;
+            }
+        }
+        catch (Exception e)
+        {
+           error = e;
+        }
+        finally
+        {
+            if (in != null) IO.close(in);
+            
+            if (error != null)
+            {
+                if (isDeleteUnrestorableSessions() && file.exists() && file.getParentFile().equals(_storeDir) )
+                {
+                    file.delete();
+                    __log.warn("Deleting file for unrestorable session "+idInCuster, error);
+                }
+                else
+                {
+                    __log.warn("Problem restoring session "+idInCuster, error);
+                }
+            }
+            else
+               file.delete(); //delete successfully restored file
+                
+        }
+        return null;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void saveSessions(boolean reactivate) throws Exception
+    {
+        if (_storeDir==null || !_storeDir.exists())
+        {
+            return;
+        }
+
+        if (!_storeDir.canWrite())
+        {
+            __log.warn ("Unable to save Sessions: Session persistence storage directory "+_storeDir.getAbsolutePath()+ " is not writeable");
+            return;
+        }
+
+        for (HashedSession session : _sessions.values())
+            session.save(true);
+    }
+
+    /* ------------------------------------------------------------ */
+    public HashedSession restoreSession (InputStream is, HashedSession session) throws Exception
+    {
+        /*
+         * Take care of this class's fields first by calling
+         * defaultReadObject
+         */
+        DataInputStream in = new DataInputStream(is);
+        try
+        {
+            String clusterId = in.readUTF();
+            in.readUTF(); // nodeId
+            long created = in.readLong();
+            long accessed = in.readLong();
+            int requests = in.readInt();
+
+            if (session == null)
+                session = (HashedSession)newSession(created, accessed, clusterId);
+            session.setRequests(requests);
+            int size = in.readInt();
+            if (size>0)
+            {
+                ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(in);
+                try
+                {
+                    for (int i=0; i<size;i++)
+                    {
+                        String key = ois.readUTF();
+                        Object value = ois.readObject();
+                        session.setAttribute(key,value);
+                    }
+                }
+                finally
+                {
+                    IO.close(ois);
+                }
+            }
+            return session;
+        }
+        finally
+        {
+            IO.close(in);
+        }
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    protected class ClassLoadingObjectInputStream extends ObjectInputStream
+    {
+        /* ------------------------------------------------------------ */
+        public ClassLoadingObjectInputStream(java.io.InputStream in) throws IOException
+        {
+            super(in);
+        }
+
+        /* ------------------------------------------------------------ */
+        public ClassLoadingObjectInputStream () throws IOException
+        {
+            super();
+        }
+
+        /* ------------------------------------------------------------ */
+        @Override
+        public Class<?> resolveClass (java.io.ObjectStreamClass cl) throws IOException, ClassNotFoundException
+        {
+            try
+            {
+                return Class.forName(cl.getName(), false, Thread.currentThread().getContextClassLoader());
+            }
+            catch (ClassNotFoundException e)
+            {
+                return super.resolveClass(cl);
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/session/HashedSession.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,241 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.util.Enumeration;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+public class HashedSession extends AbstractSession
+{
+    private static final Logger LOG = Log.getLogger(HashedSession.class);
+
+    private final HashSessionManager _hashSessionManager;
+
+    /** Whether the session has been saved because it has been deemed idle; 
+     * in which case its attribute map will have been saved and cleared. */
+    private transient boolean _idled = false;
+
+    /** Whether there has already been an attempt to save this session
+     * which has failed.  If there has, there will be no more save attempts
+     * for this session.  This is to stop the logs being flooded with errors
+     * due to serialization failures that are most likely caused by user
+     * data stored in the session that is not serializable. */
+    private transient boolean _saveFailed = false;
+
+    /* ------------------------------------------------------------- */
+    protected HashedSession(HashSessionManager hashSessionManager, HttpServletRequest request)
+    {
+        super(hashSessionManager,request);
+        _hashSessionManager = hashSessionManager;
+    }
+
+    /* ------------------------------------------------------------- */
+    protected HashedSession(HashSessionManager hashSessionManager, long created, long accessed, String clusterId)
+    {
+        super(hashSessionManager,created, accessed, clusterId);
+        _hashSessionManager = hashSessionManager;
+    }
+
+    /* ------------------------------------------------------------- */
+    protected void checkValid()
+    {
+        if (_hashSessionManager._idleSavePeriodMs!=0)
+            deIdle();
+        super.checkValid();
+    }
+    
+    /* ------------------------------------------------------------- */
+    @Override
+    public void setMaxInactiveInterval(int secs)
+    {
+        super.setMaxInactiveInterval(secs);
+        if (getMaxInactiveInterval()>0&&(getMaxInactiveInterval()*1000L/10)<_hashSessionManager._scavengePeriodMs)
+            _hashSessionManager.setScavengePeriod((secs+9)/10);
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    protected void doInvalidate()
+    throws IllegalStateException
+    {
+        super.doInvalidate();
+        
+        // Remove from the disk
+        if (_hashSessionManager._storeDir!=null && getId()!=null)
+        {
+            String id=getId();
+            File f = new File(_hashSessionManager._storeDir, id);
+            f.delete();
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    synchronized void save(boolean reactivate)
+    throws Exception
+    {
+        // Only idle the session if not already idled and no previous save/idle has failed
+        if (!isIdled() && !_saveFailed)
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug("Saving {} {}",super.getId(),reactivate);
+
+            File file = null;
+            FileOutputStream fos = null;
+            
+            try
+            {
+                file = new File(_hashSessionManager._storeDir, super.getId());
+
+                if (file.exists())
+                    file.delete();
+                file.createNewFile();
+                fos = new FileOutputStream(file);
+                willPassivate();
+                save(fos);
+                IO.close(fos);
+                if (reactivate)
+                    didActivate();
+                else
+                    clearAttributes();
+            }
+            catch (Exception e)
+            {
+                saveFailed(); // We won't try again for this session
+                if (fos != null) IO.close(fos);
+                if (file != null) file.delete(); // No point keeping the file if we didn't save the whole session
+                throw e;             
+            }
+        }
+    }
+    /* ------------------------------------------------------------ */
+    public synchronized void save(OutputStream os)  throws IOException 
+    {
+        DataOutputStream out = new DataOutputStream(os);
+        out.writeUTF(getClusterId());
+        out.writeUTF(getNodeId());
+        out.writeLong(getCreationTime());
+        out.writeLong(getAccessed());
+        
+        /* Don't write these out, as they don't make sense to store because they
+         * either they cannot be true or their value will be restored in the 
+         * Session constructor.
+         */
+        //out.writeBoolean(_invalid);
+        //out.writeBoolean(_doInvalidate);
+        //out.writeLong(_maxIdleMs);
+        //out.writeBoolean( _newSession);
+        out.writeInt(getRequests());
+        out.writeInt(getAttributes());
+        ObjectOutputStream oos = new ObjectOutputStream(out);
+        Enumeration<String> e=getAttributeNames();
+        while(e.hasMoreElements())
+        {
+            String key=e.nextElement();
+            oos.writeUTF(key);
+            oos.writeObject(doGet(key));
+        }
+        oos.close();
+    }
+
+    /* ------------------------------------------------------------ */
+    public synchronized void deIdle()
+    {
+        if (isIdled())
+        {
+            // Access now to prevent race with idling period
+            access(System.currentTimeMillis());
+
+            if (LOG.isDebugEnabled())
+                LOG.debug("De-idling " + super.getId());
+
+            FileInputStream fis = null;
+
+            try
+            {
+                File file = new File(_hashSessionManager._storeDir, super.getId());
+                if (!file.exists() || !file.canRead())
+                    throw new FileNotFoundException(file.getName());
+
+                fis = new FileInputStream(file);
+                _idled = false;
+                _hashSessionManager.restoreSession(fis, this);
+                IO.close(fis); 
+                
+                didActivate();
+
+                // If we are doing period saves, then there is no point deleting at this point 
+                if (_hashSessionManager._savePeriodMs == 0)
+                    file.delete();
+            }
+            catch (Exception e)
+            {
+                LOG.warn("Problem de-idling session " + super.getId(), e);
+                if (fis != null) IO.close(fis);//Must ensure closed before invalidate
+                invalidate();
+            }
+        }
+    }
+
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Idle the session to reduce session memory footprint.
+     * 
+     * The session is idled by persisting it, then clearing the session values attribute map and finally setting 
+     * it to an idled state.  
+     */
+    public synchronized void idle()
+    throws Exception
+    {
+        save(false);
+        _idled = true;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public synchronized boolean isIdled()
+    {
+      return _idled;
+    }
+
+    /* ------------------------------------------------------------ */
+    public synchronized boolean isSaveFailed()
+    {
+        return _saveFailed;
+    }
+
+    /* ------------------------------------------------------------ */
+    public synchronized void saveFailed()
+    {
+        _saveFailed = true;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/session/JDBCSessionIdManager.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,1040 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.sql.Blob;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.Driver;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Random;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import javax.naming.InitialContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import javax.sql.DataSource;
+
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.SessionManager;
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.util.log.Logger;
+
+
+
+/**
+ * JDBCSessionIdManager
+ *
+ * SessionIdManager implementation that uses a database to store in-use session ids, 
+ * to support distributed sessions.
+ * 
+ */
+public class JDBCSessionIdManager extends AbstractSessionIdManager
+{    
+    final static Logger LOG = SessionHandler.LOG;
+    
+    protected final HashSet<String> _sessionIds = new HashSet<String>();
+    protected Server _server;
+    protected Driver _driver;
+    protected String _driverClassName;
+    protected String _connectionUrl;
+    protected DataSource _datasource;
+    protected String _jndiName;
+    protected String _sessionIdTable = "JettySessionIds";
+    protected String _sessionTable = "JettySessions";
+    protected String _sessionTableRowId = "rowId";
+    
+    protected Timer _timer; //scavenge timer
+    protected TimerTask _task; //scavenge task
+    protected long _lastScavengeTime;
+    protected long _scavengeIntervalMs = 1000L * 60 * 10; //10mins
+    protected String _blobType; //if not set, is deduced from the type of the database at runtime
+    protected String _longType; //if not set, is deduced from the type of the database at runtime
+    
+    protected String _createSessionIdTable;
+    protected String _createSessionTable;
+                                            
+    protected String _selectBoundedExpiredSessions;
+    protected String _deleteOldExpiredSessions;
+
+    protected String _insertId;
+    protected String _deleteId;
+    protected String _queryId;
+    
+    protected  String _insertSession;
+    protected  String _deleteSession;
+    protected  String _updateSession;
+    protected  String _updateSessionNode;
+    protected  String _updateSessionAccessTime;
+    
+    protected DatabaseAdaptor _dbAdaptor;
+
+    private String _selectExpiredSessions;
+
+    
+    /**
+     * DatabaseAdaptor
+     *
+     * Handles differences between databases.
+     * 
+     * Postgres uses the getBytes and setBinaryStream methods to access
+     * a "bytea" datatype, which can be up to 1Gb of binary data. MySQL
+     * is happy to use the "blob" type and getBlob() methods instead.
+     * 
+     * TODO if the differences become more major it would be worthwhile
+     * refactoring this class.
+     */
+    public class DatabaseAdaptor 
+    {
+        String _dbName;
+        boolean _isLower;
+        boolean _isUpper;
+       
+        
+        
+        public DatabaseAdaptor (DatabaseMetaData dbMeta)
+        throws SQLException
+        {
+            _dbName = dbMeta.getDatabaseProductName().toLowerCase(Locale.ENGLISH); 
+            LOG.debug ("Using database {}",_dbName);
+            _isLower = dbMeta.storesLowerCaseIdentifiers();
+            _isUpper = dbMeta.storesUpperCaseIdentifiers();            
+        }
+        
+        /**
+         * Convert a camel case identifier into either upper or lower
+         * depending on the way the db stores identifiers.
+         * 
+         * @param identifier
+         * @return the converted identifier
+         */
+        public String convertIdentifier (String identifier)
+        {
+            if (_isLower)
+                return identifier.toLowerCase(Locale.ENGLISH);
+            if (_isUpper)
+                return identifier.toUpperCase(Locale.ENGLISH);
+            
+            return identifier;
+        }
+        
+        public String getDBName ()
+        {
+            return _dbName;
+        }
+        
+        public String getBlobType ()
+        {
+            if (_blobType != null)
+                return _blobType;
+            
+            if (_dbName.startsWith("postgres"))
+                return "bytea";
+            
+            return "blob";
+        }
+        
+        public String getLongType ()
+        {
+            if (_longType != null)
+                return _longType;
+            
+            if (_dbName.startsWith("oracle"))
+                return "number(20)";
+            
+            return "bigint";
+        }
+        
+        public InputStream getBlobInputStream (ResultSet result, String columnName)
+        throws SQLException
+        {
+            if (_dbName.startsWith("postgres"))
+            {
+                byte[] bytes = result.getBytes(columnName);
+                return new ByteArrayInputStream(bytes);
+            }
+            
+            Blob blob = result.getBlob(columnName);
+            return blob.getBinaryStream();
+        }
+        
+        /**
+         * rowId is a reserved word for Oracle, so change the name of this column
+         * @return
+         */
+        public String getRowIdColumnName ()
+        {
+            if (_dbName != null && _dbName.startsWith("oracle"))
+                return "srowId";
+            
+            return "rowId";
+        }
+        
+        
+        public boolean isEmptyStringNull ()
+        {
+            return (_dbName.startsWith("oracle"));
+        }
+        
+        public PreparedStatement getLoadStatement (Connection connection, String rowId, String contextPath, String virtualHosts) 
+        throws SQLException
+        {
+            if (contextPath == null || "".equals(contextPath))
+            {
+                if (isEmptyStringNull())
+                {
+                    PreparedStatement statement = connection.prepareStatement("select * from "+_sessionTable+
+                    " where sessionId = ? and contextPath is null and virtualHost = ?");
+                    statement.setString(1, rowId);
+                    statement.setString(2, virtualHosts);
+
+                    return statement;
+                }
+            }
+           
+
+
+            PreparedStatement statement = connection.prepareStatement("select * from "+_sessionTable+
+            " where sessionId = ? and contextPath = ? and virtualHost = ?");
+            statement.setString(1, rowId);
+            statement.setString(2, contextPath);
+            statement.setString(3, virtualHosts);
+
+            return statement;
+        }
+    }
+    
+    
+    
+    public JDBCSessionIdManager(Server server)
+    {
+        super();
+        _server=server;
+    }
+    
+    public JDBCSessionIdManager(Server server, Random random)
+    {
+       super(random);
+       _server=server;
+    }
+
+    /**
+     * Configure jdbc connection information via a jdbc Driver
+     * 
+     * @param driverClassName
+     * @param connectionUrl
+     */
+    public void setDriverInfo (String driverClassName, String connectionUrl)
+    {
+        _driverClassName=driverClassName;
+        _connectionUrl=connectionUrl;
+    }
+    
+    /**
+     * Configure jdbc connection information via a jdbc Driver
+     * 
+     * @param driverClass
+     * @param connectionUrl
+     */
+    public void setDriverInfo (Driver driverClass, String connectionUrl)
+    {
+        _driver=driverClass;
+        _connectionUrl=connectionUrl;
+    }
+    
+    
+    public void setDatasource (DataSource ds)
+    {
+        _datasource = ds;
+    }
+    
+    public DataSource getDataSource ()
+    {
+        return _datasource;
+    }
+    
+    public String getDriverClassName()
+    {
+        return _driverClassName;
+    }
+    
+    public String getConnectionUrl ()
+    {
+        return _connectionUrl;
+    }
+    
+    public void setDatasourceName (String jndi)
+    {
+        _jndiName=jndi;
+    }
+    
+    public String getDatasourceName ()
+    {
+        return _jndiName;
+    }
+   
+    public void setBlobType (String name)
+    {
+        _blobType = name;
+    }
+    
+    public String getBlobType ()
+    {
+        return _blobType;
+    }
+    
+    
+    
+    public String getLongType()
+    {
+        return _longType;
+    }
+
+    public void setLongType(String longType)
+    {
+        this._longType = longType;
+    }
+
+    public void setScavengeInterval (long sec)
+    {
+        if (sec<=0)
+            sec=60;
+
+        long old_period=_scavengeIntervalMs;
+        long period=sec*1000L;
+      
+        _scavengeIntervalMs=period;
+        
+        //add a bit of variability into the scavenge time so that not all
+        //nodes with the same scavenge time sync up
+        long tenPercent = _scavengeIntervalMs/10;
+        if ((System.currentTimeMillis()%2) == 0)
+            _scavengeIntervalMs += tenPercent;
+        
+        if (LOG.isDebugEnabled()) 
+            LOG.debug("Scavenging every "+_scavengeIntervalMs+" ms");
+        if (_timer!=null && (period!=old_period || _task==null))
+        {
+            synchronized (this)
+            {
+                if (_task!=null)
+                    _task.cancel();
+                _task = new TimerTask()
+                {
+                    @Override
+                    public void run()
+                    {
+                        scavenge();
+                    }   
+                };
+                _timer.schedule(_task,_scavengeIntervalMs,_scavengeIntervalMs);
+            }
+        }  
+    }
+    
+    public long getScavengeInterval ()
+    {
+        return _scavengeIntervalMs/1000;
+    }
+    
+    
+    public void addSession(HttpSession session)
+    {
+        if (session == null)
+            return;
+        
+        synchronized (_sessionIds)
+        {
+            String id = ((JDBCSessionManager.Session)session).getClusterId();            
+            try
+            {
+                insert(id);
+                _sessionIds.add(id);
+            }
+            catch (Exception e)
+            {
+                LOG.warn("Problem storing session id="+id, e);
+            }
+        }
+    }
+    
+    public void removeSession(HttpSession session)
+    {
+        if (session == null)
+            return;
+        
+        removeSession(((JDBCSessionManager.Session)session).getClusterId());
+    }
+    
+    
+    
+    public void removeSession (String id)
+    {
+
+        if (id == null)
+            return;
+        
+        synchronized (_sessionIds)
+        {  
+            if (LOG.isDebugEnabled())
+                LOG.debug("Removing session id="+id);
+            try
+            {               
+                _sessionIds.remove(id);
+                delete(id);
+            }
+            catch (Exception e)
+            {
+                LOG.warn("Problem removing session id="+id, e);
+            }
+        }
+        
+    }
+    
+
+    /** 
+     * Get the session id without any node identifier suffix.
+     * 
+     * @see org.eclipse.jetty.server.SessionIdManager#getClusterId(java.lang.String)
+     */
+    public String getClusterId(String nodeId)
+    {
+        int dot=nodeId.lastIndexOf('.');
+        return (dot>0)?nodeId.substring(0,dot):nodeId;
+    }
+    
+
+    /** 
+     * Get the session id, including this node's id as a suffix.
+     * 
+     * @see org.eclipse.jetty.server.SessionIdManager#getNodeId(java.lang.String, javax.servlet.http.HttpServletRequest)
+     */
+    public String getNodeId(String clusterId, HttpServletRequest request)
+    {
+        if (_workerName!=null)
+            return clusterId+'.'+_workerName;
+
+        return clusterId;
+    }
+
+
+    public boolean idInUse(String id)
+    {
+        if (id == null)
+            return false;
+        
+        String clusterId = getClusterId(id);
+        boolean inUse = false;
+        synchronized (_sessionIds)
+        {
+            inUse = _sessionIds.contains(clusterId);
+        }
+        
+        
+        if (inUse)
+            return true; //optimisation - if this session is one we've been managing, we can check locally
+
+        //otherwise, we need to go to the database to check
+        try
+        {
+            return exists(clusterId);
+        }
+        catch (Exception e)
+        {
+            LOG.warn("Problem checking inUse for id="+clusterId, e);
+            return false;
+        }
+    }
+
+    /** 
+     * Invalidate the session matching the id on all contexts.
+     * 
+     * @see org.eclipse.jetty.server.SessionIdManager#invalidateAll(java.lang.String)
+     */
+    public void invalidateAll(String id)
+    {            
+        //take the id out of the list of known sessionids for this node
+        removeSession(id);
+        
+        synchronized (_sessionIds)
+        {
+            //tell all contexts that may have a session object with this id to
+            //get rid of them
+            Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
+            for (int i=0; contexts!=null && i<contexts.length; i++)
+            {
+                SessionHandler sessionHandler = (SessionHandler)((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
+                if (sessionHandler != null) 
+                {
+                    SessionManager manager = sessionHandler.getSessionManager();
+
+                    if (manager != null && manager instanceof JDBCSessionManager)
+                    {
+                        ((JDBCSessionManager)manager).invalidateSession(id);
+                    }
+                }
+            }
+        }
+    }
+
+
+    /** 
+     * Start up the id manager.
+     * 
+     * Makes necessary database tables and starts a Session
+     * scavenger thread.
+     */
+    @Override
+    public void doStart()
+    throws Exception
+    {           
+        initializeDatabase();
+        prepareTables();   
+        cleanExpiredSessions();
+        super.doStart();
+        if (LOG.isDebugEnabled()) 
+            LOG.debug("Scavenging interval = "+getScavengeInterval()+" sec");
+        _timer=new Timer("JDBCSessionScavenger", true);
+        setScavengeInterval(getScavengeInterval());
+    }
+
+    /** 
+     * Stop the scavenger.
+     */
+    @Override
+    public void doStop () 
+    throws Exception
+    {
+        synchronized(this)
+        {
+            if (_task!=null)
+                _task.cancel();
+            if (_timer!=null)
+                _timer.cancel();
+            _timer=null;
+        }
+        _sessionIds.clear();
+        super.doStop();
+    }
+  
+    /**
+     * Get a connection from the driver or datasource.
+     * 
+     * @return the connection for the datasource
+     * @throws SQLException
+     */
+    protected Connection getConnection ()
+    throws SQLException
+    {
+        if (_datasource != null)
+            return _datasource.getConnection();
+        else
+            return DriverManager.getConnection(_connectionUrl);
+    }
+    
+    
+   
+    
+    
+    /**
+     * Set up the tables in the database
+     * @throws SQLException
+     */
+    private void prepareTables()
+    throws SQLException
+    {
+        _createSessionIdTable = "create table "+_sessionIdTable+" (id varchar(120), primary key(id))";
+        _selectBoundedExpiredSessions = "select * from "+_sessionTable+" where expiryTime >= ? and expiryTime <= ?";
+        _selectExpiredSessions = "select * from "+_sessionTable+" where expiryTime >0 and expiryTime <= ?";
+        _deleteOldExpiredSessions = "delete from "+_sessionTable+" where expiryTime >0 and expiryTime <= ?";
+
+        _insertId = "insert into "+_sessionIdTable+" (id)  values (?)";
+        _deleteId = "delete from "+_sessionIdTable+" where id = ?";
+        _queryId = "select * from "+_sessionIdTable+" where id = ?";
+
+        Connection connection = null;
+        try
+        {
+            //make the id table
+            connection = getConnection();
+            connection.setAutoCommit(true);
+            DatabaseMetaData metaData = connection.getMetaData();
+            _dbAdaptor = new DatabaseAdaptor(metaData);
+            _sessionTableRowId = _dbAdaptor.getRowIdColumnName();
+
+            //checking for table existence is case-sensitive, but table creation is not
+            String tableName = _dbAdaptor.convertIdentifier(_sessionIdTable);
+            ResultSet result = metaData.getTables(null, null, tableName, null);
+            if (!result.next())
+            {
+                //table does not exist, so create it
+                connection.createStatement().executeUpdate(_createSessionIdTable);
+            }
+            
+            //make the session table if necessary
+            tableName = _dbAdaptor.convertIdentifier(_sessionTable);   
+            result = metaData.getTables(null, null, tableName, null);
+            if (!result.next())
+            {
+                //table does not exist, so create it
+                String blobType = _dbAdaptor.getBlobType();
+                String longType = _dbAdaptor.getLongType();
+                _createSessionTable = "create table "+_sessionTable+" ("+_sessionTableRowId+" varchar(120), sessionId varchar(120), "+
+                                           " contextPath varchar(60), virtualHost varchar(60), lastNode varchar(60), accessTime "+longType+", "+
+                                           " lastAccessTime "+longType+", createTime "+longType+", cookieTime "+longType+", "+
+                                           " lastSavedTime "+longType+", expiryTime "+longType+", map "+blobType+", primary key("+_sessionTableRowId+"))";
+                connection.createStatement().executeUpdate(_createSessionTable);
+            }
+            
+            //make some indexes on the JettySessions table
+            String index1 = "idx_"+_sessionTable+"_expiry";
+            String index2 = "idx_"+_sessionTable+"_session";
+            
+            result = metaData.getIndexInfo(null, null, tableName, false, false);
+            boolean index1Exists = false;
+            boolean index2Exists = false;
+            while (result.next())
+            {
+                String idxName = result.getString("INDEX_NAME");
+                if (index1.equalsIgnoreCase(idxName))
+                    index1Exists = true;
+                else if (index2.equalsIgnoreCase(idxName))
+                    index2Exists = true;
+            }
+            if (!(index1Exists && index2Exists))
+            {
+                Statement statement = connection.createStatement();
+                try
+                {
+                    if (!index1Exists)
+                        statement.executeUpdate("create index "+index1+" on "+_sessionTable+" (expiryTime)");
+                    if (!index2Exists)
+                        statement.executeUpdate("create index "+index2+" on "+_sessionTable+" (sessionId, contextPath)");
+                }
+                finally
+                {
+                    if (statement!=null)
+                    {
+                        try { statement.close(); }
+                        catch(Exception e) { LOG.warn(e); }
+                    }
+                }
+            }
+
+            //set up some strings representing the statements for session manipulation
+            _insertSession = "insert into "+_sessionTable+
+            " ("+_sessionTableRowId+", sessionId, contextPath, virtualHost, lastNode, accessTime, lastAccessTime, createTime, cookieTime, lastSavedTime, expiryTime, map) "+
+            " values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
+
+            _deleteSession = "delete from "+_sessionTable+
+            " where "+_sessionTableRowId+" = ?";
+            
+            _updateSession = "update "+_sessionTable+
+            " set lastNode = ?, accessTime = ?, lastAccessTime = ?, lastSavedTime = ?, expiryTime = ?, map = ? where "+_sessionTableRowId+" = ?";
+
+            _updateSessionNode = "update "+_sessionTable+
+            " set lastNode = ? where "+_sessionTableRowId+" = ?";
+
+            _updateSessionAccessTime = "update "+_sessionTable+
+            " set lastNode = ?, accessTime = ?, lastAccessTime = ?, lastSavedTime = ?, expiryTime = ? where "+_sessionTableRowId+" = ?";
+
+            
+        }
+        finally
+        {
+            if (connection != null)
+                connection.close();
+        }
+    }
+    
+    /**
+     * Insert a new used session id into the table.
+     * 
+     * @param id
+     * @throws SQLException
+     */
+    private void insert (String id)
+    throws SQLException 
+    {
+        Connection connection = null;
+        PreparedStatement statement = null;
+        PreparedStatement query = null;
+        try
+        {
+            connection = getConnection();
+            connection.setAutoCommit(true);            
+            query = connection.prepareStatement(_queryId);
+            query.setString(1, id);
+            ResultSet result = query.executeQuery();
+            //only insert the id if it isn't in the db already 
+            if (!result.next())
+            {
+                statement = connection.prepareStatement(_insertId);
+                statement.setString(1, id);
+                statement.executeUpdate();
+            }
+        }
+        finally
+        {
+            if (query!=null)
+            {
+                try { query.close(); }
+                catch(Exception e) { LOG.warn(e); }
+            }
+
+            if (statement!=null)
+            {
+                try { statement.close(); }
+                catch(Exception e) { LOG.warn(e); }
+            }
+
+            if (connection != null)
+                connection.close();
+        }
+    }
+    
+    /**
+     * Remove a session id from the table.
+     * 
+     * @param id
+     * @throws SQLException
+     */
+    private void delete (String id)
+    throws SQLException
+    {
+        Connection connection = null;
+        PreparedStatement statement = null;
+        try
+        {
+            connection = getConnection();
+            connection.setAutoCommit(true);
+            statement = connection.prepareStatement(_deleteId);
+            statement.setString(1, id);
+            statement.executeUpdate();
+        }
+        finally
+        {
+            if (statement!=null)
+            {
+                try { statement.close(); }
+                catch(Exception e) { LOG.warn(e); }
+            }
+
+            if (connection != null)
+                connection.close();
+        }
+    }
+    
+    
+    /**
+     * Check if a session id exists.
+     * 
+     * @param id
+     * @return
+     * @throws SQLException
+     */
+    private boolean exists (String id)
+    throws SQLException
+    {
+        Connection connection = null;
+        PreparedStatement statement = null;
+        try
+        {
+            connection = getConnection();
+            connection.setAutoCommit(true);
+            statement = connection.prepareStatement(_queryId);
+            statement.setString(1, id);
+            ResultSet result = statement.executeQuery();
+            return result.next();
+        }
+        finally
+        {
+            if (statement!=null)
+            {
+                try { statement.close(); }
+                catch(Exception e) { LOG.warn(e); }
+            }
+
+            if (connection != null)
+                connection.close();
+        }
+    }
+    
+    /**
+     * Look for sessions in the database that have expired.
+     * 
+     * We do this in the SessionIdManager and not the SessionManager so
+     * that we only have 1 scavenger, otherwise if there are n SessionManagers
+     * there would be n scavengers, all contending for the database.
+     * 
+     * We look first for sessions that expired in the previous interval, then
+     * for sessions that expired previously - these are old sessions that no
+     * node is managing any more and have become stuck in the database.
+     */
+    private void scavenge ()
+    {
+        Connection connection = null;
+        PreparedStatement statement = null;
+        List<String> expiredSessionIds = new ArrayList<String>();
+        try
+        {            
+            if (LOG.isDebugEnabled()) 
+                LOG.debug("Scavenge sweep started at "+System.currentTimeMillis());
+            if (_lastScavengeTime > 0)
+            {
+                connection = getConnection();
+                connection.setAutoCommit(true);
+                //"select sessionId from JettySessions where expiryTime > (lastScavengeTime - scanInterval) and expiryTime < lastScavengeTime";
+                statement = connection.prepareStatement(_selectBoundedExpiredSessions);
+                long lowerBound = (_lastScavengeTime - _scavengeIntervalMs);
+                long upperBound = _lastScavengeTime;
+                if (LOG.isDebugEnabled()) 
+                    LOG.debug (" Searching for sessions expired between "+lowerBound + " and "+upperBound);
+                
+                statement.setLong(1, lowerBound);
+                statement.setLong(2, upperBound);
+                ResultSet result = statement.executeQuery();
+                while (result.next())
+                {
+                    String sessionId = result.getString("sessionId");
+                    expiredSessionIds.add(sessionId);
+                    if (LOG.isDebugEnabled()) LOG.debug (" Found expired sessionId="+sessionId); 
+                }
+
+                //tell the SessionManagers to expire any sessions with a matching sessionId in memory
+                Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
+                for (int i=0; contexts!=null && i<contexts.length; i++)
+                {
+
+                    SessionHandler sessionHandler = (SessionHandler)((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
+                    if (sessionHandler != null) 
+                    { 
+                        SessionManager manager = sessionHandler.getSessionManager();
+                        if (manager != null && manager instanceof JDBCSessionManager)
+                        {
+                            ((JDBCSessionManager)manager).expire(expiredSessionIds);
+                        }
+                    }
+                }
+
+                //find all sessions that have expired at least a couple of scanIntervals ago and just delete them
+                upperBound = _lastScavengeTime - (2 * _scavengeIntervalMs);
+                if (upperBound > 0)
+                {
+                    if (LOG.isDebugEnabled()) LOG.debug("Deleting old expired sessions expired before "+upperBound);
+                    try
+                    {
+                        statement = connection.prepareStatement(_deleteOldExpiredSessions);
+                        statement.setLong(1, upperBound);
+                        int rows = statement.executeUpdate();
+                        if (LOG.isDebugEnabled()) LOG.debug("Deleted "+rows+" rows of old sessions expired before "+upperBound);
+                    }
+                    finally
+                    {
+                        if (statement!=null)
+                        {
+                            try { statement.close(); }
+                            catch(Exception e) { LOG.warn(e); }
+                        }
+                    }
+                }
+            }
+        }
+        catch (Exception e)
+        {
+            if (isRunning())    
+                LOG.warn("Problem selecting expired sessions", e);
+            else
+                LOG.ignore(e);
+        }
+        finally
+        {           
+            _lastScavengeTime=System.currentTimeMillis();
+            if (LOG.isDebugEnabled()) LOG.debug("Scavenge sweep ended at "+_lastScavengeTime);
+            if (connection != null)
+            {
+                try
+                {
+                connection.close();
+                }
+                catch (SQLException e)
+                {
+                    LOG.warn(e);
+                }
+            }
+        }
+    }
+    
+    /**
+     * Get rid of sessions and sessionids from sessions that have already expired
+     * @throws Exception
+     */
+    private void cleanExpiredSessions ()
+    {
+        Connection connection = null;
+        PreparedStatement statement = null;
+        Statement sessionsTableStatement = null;
+        Statement sessionIdsTableStatement = null;
+        List<String> expiredSessionIds = new ArrayList<String>();
+        try
+        {     
+            connection = getConnection();
+            connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
+            connection.setAutoCommit(false);
+
+            statement = connection.prepareStatement(_selectExpiredSessions);
+            long now = System.currentTimeMillis();
+            if (LOG.isDebugEnabled()) LOG.debug ("Searching for sessions expired before {}", now);
+
+            statement.setLong(1, now);
+            ResultSet result = statement.executeQuery();
+            while (result.next())
+            {
+                String sessionId = result.getString("sessionId");
+                expiredSessionIds.add(sessionId);
+                if (LOG.isDebugEnabled()) LOG.debug ("Found expired sessionId={}", sessionId); 
+            }
+            
+            sessionsTableStatement = null;
+            sessionIdsTableStatement = null;
+
+            if (!expiredSessionIds.isEmpty())
+            {
+                sessionsTableStatement = connection.createStatement();
+                sessionsTableStatement.executeUpdate(createCleanExpiredSessionsSql("delete from "+_sessionTable+" where sessionId in ", expiredSessionIds));
+                sessionIdsTableStatement = connection.createStatement();
+                sessionIdsTableStatement.executeUpdate(createCleanExpiredSessionsSql("delete from "+_sessionIdTable+" where id in ", expiredSessionIds));
+            }
+            connection.commit();
+
+            synchronized (_sessionIds)
+            {
+                _sessionIds.removeAll(expiredSessionIds); //in case they were in our local cache of session ids
+            }
+        }
+        catch (Exception e)
+        {
+            if (connection != null)
+            {
+                try 
+                { 
+                    LOG.warn("Rolling back clean of expired sessions", e);
+                    connection.rollback();
+                }
+                catch (Exception x) { LOG.warn("Rollback of expired sessions failed", x);}
+            }
+        }
+        finally
+        {
+            if (sessionIdsTableStatement!=null)
+            {
+                try { sessionIdsTableStatement.close(); }
+                catch(Exception e) { LOG.warn(e); }
+            }
+
+            if (sessionsTableStatement!=null)
+            {
+                try { sessionsTableStatement.close(); }
+                catch(Exception e) { LOG.warn(e); }
+            }
+
+            if (statement!=null)
+            {
+                try { statement.close(); }
+                catch(Exception e) { LOG.warn(e); }
+            }
+
+            try
+            {
+                if (connection != null)
+                    connection.close();
+            }
+            catch (SQLException e)
+            {
+                LOG.warn(e);
+            }
+        }
+    }
+    
+    
+    /**
+     * 
+     * @param sql
+     * @param connection
+     * @param expiredSessionIds
+     * @throws Exception
+     */
+    private String createCleanExpiredSessionsSql (String sql,Collection<String> expiredSessionIds)
+    throws Exception
+    {
+        StringBuffer buff = new StringBuffer();
+        buff.append(sql);
+        buff.append("(");
+        Iterator<String> itor = expiredSessionIds.iterator();
+        while (itor.hasNext())
+        {
+            buff.append("'"+(itor.next())+"'");
+            if (itor.hasNext())
+                buff.append(",");
+        }
+        buff.append(")");
+        
+        if (LOG.isDebugEnabled()) LOG.debug("Cleaning expired sessions with: {}", buff);
+        return buff.toString();
+    }
+    
+    private void initializeDatabase ()
+    throws Exception
+    {
+        if (_datasource != null)
+            return; //already set up
+        
+        if (_jndiName!=null)
+        {
+            InitialContext ic = new InitialContext();
+            _datasource = (DataSource)ic.lookup(_jndiName);
+        }
+        else if ( _driver != null && _connectionUrl != null )
+        {
+            DriverManager.registerDriver(_driver);
+        }
+        else if (_driverClassName != null && _connectionUrl != null)
+        {
+            Class.forName(_driverClassName);
+        }
+        else
+            throw new IllegalStateException("No database configured for sessions");
+    }
+    
+   
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/session/JDBCSessionManager.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,1174 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.servlet.SessionTrackingMode;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSessionEvent;
+import javax.servlet.http.HttpSessionListener;
+
+import org.eclipse.jetty.server.SessionIdManager;
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * JDBCSessionManager
+ *
+ * SessionManager that persists sessions to a database to enable clustering.
+ *
+ * Session data is persisted to the JettySessions table:
+ *
+ * rowId (unique in cluster: webapp name/path + virtualhost + sessionId)
+ * contextPath (of the context owning the session)
+ * sessionId (unique in a context)
+ * lastNode (name of node last handled session)
+ * accessTime (time in milliseconds session was accessed)
+ * lastAccessTime (previous time in milliseconds session was accessed)
+ * createTime (time in milliseconds session created)
+ * cookieTime (time in milliseconds session cookie created)
+ * lastSavedTime (last time in milliseconds session access times were saved)
+ * expiryTime (time in milliseconds that the session is due to expire)
+ * map (attribute map)
+ *
+ * As an optimization, to prevent thrashing the database, we do not persist
+ * the accessTime and lastAccessTime every time the session is accessed. Rather,
+ * we write it out every so often. The frequency is controlled by the saveIntervalSec
+ * field.
+ */
+public class JDBCSessionManager extends AbstractSessionManager
+{
+    private static final Logger LOG = Log.getLogger(JDBCSessionManager.class);
+
+    private ConcurrentHashMap<String, AbstractSession> _sessions;
+    protected JDBCSessionIdManager _jdbcSessionIdMgr = null;
+    protected long _saveIntervalSec = 60; //only persist changes to session access times every 60 secs
+
+   
+
+
+    /**
+     * Session
+     *
+     * Session instance.
+     */
+    public class Session extends AbstractSession
+    {
+        private static final long serialVersionUID = 5208464051134226143L;
+        
+        /**
+         * If dirty, session needs to be (re)persisted
+         */
+        private boolean _dirty=false;
+        
+        
+        /**
+         * Time in msec since the epoch that a session cookie was set for this session
+         */
+        private long _cookieSet;
+        
+        
+        /**
+         * Time in msec since the epoch that the session will expire
+         */
+        private long _expiryTime;
+        
+        
+        /**
+         * Time in msec since the epoch that the session was last persisted
+         */
+        private long _lastSaved;
+        
+        
+        /**
+         * Unique identifier of the last node to host the session
+         */
+        private String _lastNode;
+        
+        
+        /**
+         * Virtual host for context (used to help distinguish 2 sessions with same id on different contexts)
+         */
+        private String _virtualHost;
+        
+        
+        /**
+         * Unique row in db for session
+         */
+        private String _rowId;
+        
+        
+        /**
+         * Mangled context name (used to help distinguish 2 sessions with same id on different contexts)
+         */
+        private String _canonicalContext;
+        
+   
+        /**
+         * Session from a request.
+         *
+         * @param request
+         */
+        protected Session (HttpServletRequest request)
+        {
+            super(JDBCSessionManager.this,request);
+            int maxInterval=getMaxInactiveInterval();
+            _expiryTime = (maxInterval <= 0 ? 0 : (System.currentTimeMillis() + maxInterval*1000L));
+            _virtualHost = JDBCSessionManager.getVirtualHost(_context);
+            _canonicalContext = canonicalize(_context.getContextPath());
+            _lastNode = getSessionIdManager().getWorkerName();
+        }
+        
+        
+        /**
+         * Session restored from database
+         * @param sessionId
+         * @param rowId
+         * @param created
+         * @param accessed
+         */
+        protected Session (String sessionId, String rowId, long created, long accessed)
+        {
+            super(JDBCSessionManager.this, created, accessed, sessionId);
+            _rowId = rowId;
+        }
+        
+        
+        protected synchronized String getRowId()
+        {
+            return _rowId;
+        }
+        
+        protected synchronized void setRowId(String rowId)
+        {
+            _rowId = rowId;
+        }
+        
+        public synchronized void setVirtualHost (String vhost)
+        {
+            _virtualHost=vhost;
+        }
+
+        public synchronized String getVirtualHost ()
+        {
+            return _virtualHost;
+        }
+        
+        public synchronized long getLastSaved ()
+        {
+            return _lastSaved;
+        }
+
+        public synchronized void setLastSaved (long time)
+        {
+            _lastSaved=time;
+        }
+
+        public synchronized void setExpiryTime (long time)
+        {
+            _expiryTime=time;
+        }
+
+        public synchronized long getExpiryTime ()
+        {
+            return _expiryTime;
+        }
+        
+
+        public synchronized void setCanonicalContext(String str)
+        {
+            _canonicalContext=str;
+        }
+
+        public synchronized String getCanonicalContext ()
+        {
+            return _canonicalContext;
+        }
+        
+        public void setCookieSet (long ms)
+        {
+            _cookieSet = ms;
+        }
+
+        public synchronized long getCookieSet ()
+        {
+            return _cookieSet;
+        }
+
+        public synchronized void setLastNode (String node)
+        {
+            _lastNode=node;
+        }
+
+        public synchronized String getLastNode ()
+        {
+            return _lastNode;
+        }
+
+        @Override
+        public void setAttribute (String name, Object value)
+        {
+            super.setAttribute(name, value);
+            _dirty=true;
+        }
+
+        @Override
+        public void removeAttribute (String name)
+        {
+            super.removeAttribute(name);
+            _dirty=true;
+        }
+
+        @Override
+        protected void cookieSet()
+        {
+            _cookieSet = getAccessed();
+        }
+
+        /**
+         * Entry to session.
+         * Called by SessionHandler on inbound request and the session already exists in this node's memory.
+         *
+         * @see org.eclipse.jetty.server.session.AbstractSession#access(long)
+         */
+        @Override
+        protected boolean access(long time)
+        {
+            synchronized (this)
+            {
+                if (super.access(time))
+                {
+                    int maxInterval=getMaxInactiveInterval();
+                    _expiryTime = (maxInterval <= 0 ? 0 : (time + maxInterval*1000L));
+                    return true;
+                }
+                return false;
+            }
+        }
+        
+
+
+        /**
+         * Exit from session
+         * @see org.eclipse.jetty.server.session.AbstractSession#complete()
+         */
+        @Override
+        protected void complete()
+        {
+            synchronized (this)
+            {
+                super.complete();
+                try
+                {
+                    if (isValid())
+                    {
+                        if (_dirty)
+                        {
+                            //The session attributes have changed, write to the db, ensuring
+                            //http passivation/activation listeners called
+                            willPassivate();                      
+                            updateSession(this);
+                            didActivate();
+                        }
+                        else if ((getAccessed() - _lastSaved) >= (getSaveInterval() * 1000L))
+                        {
+                            updateSessionAccessTime(this);
+                        }
+                    }
+                }
+                catch (Exception e)
+                {
+                    LOG.warn("Problem persisting changed session data id="+getId(), e);
+                }
+                finally
+                {
+                    _dirty=false;
+                }
+            }
+        }
+
+        @Override
+        protected void timeout() throws IllegalStateException
+        {
+            if (LOG.isDebugEnabled())
+                LOG.debug("Timing out session id="+getClusterId());
+            super.timeout();
+        }
+        
+        @Override
+        public String toString ()
+        {
+            return "Session rowId="+_rowId+",id="+getId()+",lastNode="+_lastNode+
+                            ",created="+getCreationTime()+",accessed="+getAccessed()+
+                            ",lastAccessed="+getLastAccessedTime()+",cookieSet="+_cookieSet+
+                            ",lastSaved="+_lastSaved+",expiry="+_expiryTime;
+        }
+    }
+
+
+
+
+    /**
+     * ClassLoadingObjectInputStream
+     *
+     * Used to persist the session attribute map
+     */
+    protected class ClassLoadingObjectInputStream extends ObjectInputStream
+    {
+        public ClassLoadingObjectInputStream(java.io.InputStream in) throws IOException
+        {
+            super(in);
+        }
+
+        public ClassLoadingObjectInputStream () throws IOException
+        {
+            super();
+        }
+
+        @Override
+        public Class<?> resolveClass (java.io.ObjectStreamClass cl) throws IOException, ClassNotFoundException
+        {
+            try
+            {
+                return Class.forName(cl.getName(), false, Thread.currentThread().getContextClassLoader());
+            }
+            catch (ClassNotFoundException e)
+            {
+                return super.resolveClass(cl);
+            }
+        }
+    }
+
+
+    /**
+     * Set the time in seconds which is the interval between
+     * saving the session access time to the database.
+     *
+     * This is an optimization that prevents the database from
+     * being overloaded when a session is accessed very frequently.
+     *
+     * On session exit, if the session attributes have NOT changed,
+     * the time at which we last saved the accessed
+     * time is compared to the current accessed time. If the interval
+     * is at least saveIntervalSecs, then the access time will be
+     * persisted to the database.
+     *
+     * If any session attribute does change, then the attributes and
+     * the accessed time are persisted.
+     *
+     * @param sec
+     */
+    public void setSaveInterval (long sec)
+    {
+        _saveIntervalSec=sec;
+    }
+
+    public long getSaveInterval ()
+    {
+        return _saveIntervalSec;
+    }
+
+
+
+    /**
+     * A method that can be implemented in subclasses to support
+     * distributed caching of sessions. This method will be
+     * called whenever the session is written to the database
+     * because the session data has changed.
+     *
+     * This could be used eg with a JMS backplane to notify nodes
+     * that the session has changed and to delete the session from
+     * the node's cache, and re-read it from the database.
+     * @param session
+     */
+    public void cacheInvalidate (Session session)
+    {
+
+    }
+
+
+    /**
+     * A session has been requested by its id on this node.
+     *
+     * Load the session by id AND context path from the database.
+     * Multiple contexts may share the same session id (due to dispatching)
+     * but they CANNOT share the same contents.
+     *
+     * Check if last node id is my node id, if so, then the session we have
+     * in memory cannot be stale. If another node used the session last, then
+     * we need to refresh from the db.
+     *
+     * NOTE: this method will go to the database, so if you only want to check
+     * for the existence of a Session in memory, use _sessions.get(id) instead.
+     *
+     * @see org.eclipse.jetty.server.session.AbstractSessionManager#getSession(java.lang.String)
+     */
+    @Override
+    public Session getSession(String idInCluster)
+    {
+        Session session = null;
+        Session memSession = (Session)_sessions.get(idInCluster);
+
+        synchronized (this)
+        {
+                //check if we need to reload the session -
+                //as an optimization, don't reload on every access
+                //to reduce the load on the database. This introduces a window of
+                //possibility that the node may decide that the session is local to it,
+                //when the session has actually been live on another node, and then
+                //re-migrated to this node. This should be an extremely rare occurrence,
+                //as load-balancers are generally well-behaved and consistently send
+                //sessions to the same node, changing only iff that node fails.
+                //Session data = null;
+                long now = System.currentTimeMillis();
+                if (LOG.isDebugEnabled())
+                {
+                    if (memSession==null)
+                        LOG.debug("getSession("+idInCluster+"): not in session map,"+
+                                " now="+now+
+                                " lastSaved="+(memSession==null?0:memSession._lastSaved)+
+                                " interval="+(_saveIntervalSec * 1000L));
+                    else
+                        LOG.debug("getSession("+idInCluster+"): in session map, "+
+                                " now="+now+
+                                " lastSaved="+(memSession==null?0:memSession._lastSaved)+
+                                " interval="+(_saveIntervalSec * 1000L)+
+                                " lastNode="+memSession._lastNode+
+                                " thisNode="+getSessionIdManager().getWorkerName()+
+                                " difference="+(now - memSession._lastSaved));
+                }
+
+                try
+                {
+                    if (memSession==null)
+                    {
+                        LOG.debug("getSession("+idInCluster+"): no session in session map. Reloading session data from db.");
+                        session = loadSession(idInCluster, canonicalize(_context.getContextPath()), getVirtualHost(_context));
+                    }
+                    else if ((now - memSession._lastSaved) >= (_saveIntervalSec * 1000L))
+                    {
+                        LOG.debug("getSession("+idInCluster+"): stale session. Reloading session data from db.");
+                        session = loadSession(idInCluster, canonicalize(_context.getContextPath()), getVirtualHost(_context));
+                    }
+                    else
+                    {
+                        LOG.debug("getSession("+idInCluster+"): session in session map");
+                        session = memSession;
+                    }
+                }
+                catch (Exception e)
+                {
+                    LOG.warn("Unable to load session "+idInCluster, e);
+                    return null;
+                }
+
+                
+                //If we have a session
+                if (session != null)
+                {
+                    //If the session was last used on a different node, or session doesn't exist on this node
+                    if (!session.getLastNode().equals(getSessionIdManager().getWorkerName()) || memSession==null)
+                    {
+                        //if session doesn't expire, or has not already expired, update it and put it in this nodes' memory
+                        if (session._expiryTime <= 0 || session._expiryTime > now)
+                        {
+                            if (LOG.isDebugEnabled()) 
+                                LOG.debug("getSession("+idInCluster+"): lastNode="+session.getLastNode()+" thisNode="+getSessionIdManager().getWorkerName());
+                            
+                            session.setLastNode(getSessionIdManager().getWorkerName());                            
+                            _sessions.put(idInCluster, session);
+                            
+                            //update in db: if unable to update, session will be scavenged later
+                            try
+                            {
+                                updateSessionNode(session);
+                                session.didActivate();
+                            }
+                            catch (Exception e)
+                            {
+                                LOG.warn("Unable to update freshly loaded session "+idInCluster, e);
+                                return null;
+                            }
+                        }
+                        else
+                        {
+                            LOG.debug("getSession ({}): Session has expired", idInCluster);  
+                            session=null;
+                        }
+
+                    }
+                    else
+                    {
+                       //the session loaded from the db and the one in memory are the same, so keep using the one in memory
+                       session = memSession;
+                       LOG.debug("getSession({}): Session not stale {}", idInCluster,session);
+                    }
+                }
+                else
+                {
+                    //No session in db with matching id and context path.
+                    LOG.debug("getSession({}): No session in database matching id={}",idInCluster,idInCluster);
+                }
+
+                return session;
+        }
+    }
+
+    /**
+     * Get the number of sessions.
+     *
+     * @see org.eclipse.jetty.server.session.AbstractSessionManager#getSessions()
+     */
+    @Override
+    public int getSessions()
+    {
+        int size = 0;
+        synchronized (this)
+        {
+            size = _sessions.size();
+        }
+        return size;
+    }
+
+
+    /**
+     * Start the session manager.
+     *
+     * @see org.eclipse.jetty.server.session.AbstractSessionManager#doStart()
+     */
+    @Override
+    public void doStart() throws Exception
+    {
+        if (_sessionIdManager==null)
+            throw new IllegalStateException("No session id manager defined");
+
+        _jdbcSessionIdMgr = (JDBCSessionIdManager)_sessionIdManager;
+        
+        _sessions = new ConcurrentHashMap<String, AbstractSession>();
+
+        super.doStart();
+    }
+
+
+    /**
+     * Stop the session manager.
+     *
+     * @see org.eclipse.jetty.server.session.AbstractSessionManager#doStop()
+     */
+    @Override
+    public void doStop() throws Exception
+    {
+        _sessions.clear();
+        _sessions = null;
+
+        super.doStop();
+    }
+
+    @Override
+    protected void invalidateSessions()
+    {
+        //Do nothing - we don't want to remove and
+        //invalidate all the sessions because this
+        //method is called from doStop(), and just
+        //because this context is stopping does not
+        //mean that we should remove the session from
+        //any other nodes
+    }
+
+
+    /**
+     * Invalidate a session.
+     *
+     * @param idInCluster
+     */
+    protected void invalidateSession (String idInCluster)
+    {
+        Session session = null;
+        synchronized (this)
+        {
+            session = (Session)_sessions.get(idInCluster);
+        }
+
+        if (session != null)
+        {
+            session.invalidate();
+        }
+    }
+
+    /**
+     * Delete an existing session, both from the in-memory map and
+     * the database.
+     *
+     * @see org.eclipse.jetty.server.session.AbstractSessionManager#removeSession(java.lang.String)
+     */
+    @Override
+    protected boolean removeSession(String idInCluster)
+    {
+        synchronized (this)
+        {
+            Session session = (Session)_sessions.remove(idInCluster);
+            try
+            {
+                if (session != null)
+                    deleteSession(session);
+            }
+            catch (Exception e)
+            {
+                LOG.warn("Problem deleting session id="+idInCluster, e);
+            }
+            return session!=null;
+        }
+    }
+
+
+    /**
+     * Add a newly created session to our in-memory list for this node and persist it.
+     *
+     * @see org.eclipse.jetty.server.session.AbstractSessionManager#addSession(org.eclipse.jetty.server.session.AbstractSession)
+     */
+    @Override
+    protected void addSession(AbstractSession session)
+    {
+        if (session==null)
+            return;
+
+        synchronized (this)
+        {
+            _sessions.put(session.getClusterId(), session);
+        }
+
+        //TODO or delay the store until exit out of session? If we crash before we store it
+        //then session data will be lost.
+        try
+        {
+            synchronized (session)
+            {
+                session.willPassivate();
+                storeSession(((JDBCSessionManager.Session)session));
+                session.didActivate();
+            }
+        }
+        catch (Exception e)
+        {
+            LOG.warn("Unable to store new session id="+session.getId() , e);
+        }
+    }
+
+
+    /**
+     * Make a new Session.
+     *
+     * @see org.eclipse.jetty.server.session.AbstractSessionManager#newSession(javax.servlet.http.HttpServletRequest)
+     */
+    @Override
+    protected AbstractSession newSession(HttpServletRequest request)
+    {
+        return new Session(request);
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Remove session from manager
+     * @param session The session to remove
+     * @param invalidate True if {@link HttpSessionListener#sessionDestroyed(HttpSessionEvent)} and
+     * {@link SessionIdManager#invalidateAll(String)} should be called.
+     */
+    @Override
+    public void removeSession(AbstractSession session, boolean invalidate)
+    {
+        // Remove session from context and global maps
+        boolean removed = false;
+
+        synchronized (this)
+        {
+            //take this session out of the map of sessions for this context
+            if (getSession(session.getClusterId()) != null)
+            {
+                removed = true;
+                removeSession(session.getClusterId());
+            }
+        }
+
+        if (removed)
+        {
+            // Remove session from all context and global id maps
+            _sessionIdManager.removeSession(session);
+
+            if (invalidate)
+                _sessionIdManager.invalidateAll(session.getClusterId());
+
+            if (invalidate && !_sessionListeners.isEmpty())
+            {
+                HttpSessionEvent event=new HttpSessionEvent(session);
+                for (HttpSessionListener l : _sessionListeners)
+                    l.sessionDestroyed(event);
+            }
+            if (!invalidate)
+            {
+                session.willPassivate();
+            }
+        }
+    }
+
+
+    /**
+     * Expire any Sessions we have in memory matching the list of
+     * expired Session ids.
+     *
+     * @param sessionIds
+     */
+    protected void expire (List<?> sessionIds)
+    {
+        //don't attempt to scavenge if we are shutting down
+        if (isStopping() || isStopped())
+            return;
+
+        //Remove any sessions we already have in memory that match the ids
+        Thread thread=Thread.currentThread();
+        ClassLoader old_loader=thread.getContextClassLoader();
+        ListIterator<?> itor = sessionIds.listIterator();
+
+        try
+        {
+            while (itor.hasNext())
+            {
+                String sessionId = (String)itor.next();
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Expiring session id "+sessionId);
+
+                Session session = (Session)_sessions.get(sessionId);
+                if (session != null)
+                {
+                    session.timeout();
+                    itor.remove();
+                }
+                else
+                {
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("Unrecognized session id="+sessionId);
+                }
+            }
+        }
+        catch (Throwable t)
+        {
+            LOG.warn("Problem expiring sessions", t);
+        }
+        finally
+        {
+            thread.setContextClassLoader(old_loader);
+        }
+    }
+
+
+    /**
+     * Load a session from the database
+     * @param id
+     * @return the session data that was loaded
+     * @throws Exception
+     */
+    protected Session loadSession (final String id, final String canonicalContextPath, final String vhost)
+    throws Exception
+    {
+        final AtomicReference<Session> _reference = new AtomicReference<Session>();
+        final AtomicReference<Exception> _exception = new AtomicReference<Exception>();
+        Runnable load = new Runnable()
+        {
+            @SuppressWarnings("unchecked")
+            public void run()
+            {
+                Session session = null;
+                Connection connection=null;
+                PreparedStatement statement = null;
+                try
+                {
+                    connection = getConnection();
+                    statement = _jdbcSessionIdMgr._dbAdaptor.getLoadStatement(connection, id, canonicalContextPath, vhost);
+                    ResultSet result = statement.executeQuery();
+                    if (result.next())
+                    {                    
+                        session = new Session(id, result.getString(_jdbcSessionIdMgr._sessionTableRowId), result.getLong("createTime"), result.getLong("accessTime"));
+                        session.setCookieSet(result.getLong("cookieTime"));
+                        session.setLastAccessedTime(result.getLong("lastAccessTime"));
+                        session.setLastNode(result.getString("lastNode"));
+                        session.setLastSaved(result.getLong("lastSavedTime"));
+                        session.setExpiryTime(result.getLong("expiryTime"));
+                        session.setCanonicalContext(result.getString("contextPath"));
+                        session.setVirtualHost(result.getString("virtualHost"));
+                                           
+                        InputStream is = ((JDBCSessionIdManager)getSessionIdManager())._dbAdaptor.getBlobInputStream(result, "map");
+                        ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream (is);
+                        Object o = ois.readObject();
+                        session.addAttributes((Map<String,Object>)o);
+                        ois.close();
+
+                        if (LOG.isDebugEnabled())
+                            LOG.debug("LOADED session "+session);
+                    }
+                    _reference.set(session);
+                }
+                catch (Exception e)
+                {
+                    _exception.set(e);
+                }
+                finally
+                {
+                    if (statement!=null)
+                    {
+                        try { statement.close(); }
+                        catch(Exception e) { LOG.warn(e); }
+                    }
+
+                    if (connection!=null)
+                    {
+                        try { connection.close();}
+                        catch(Exception e) { LOG.warn(e); }
+                    }
+                }
+            }
+        };
+
+        if (_context==null)
+            load.run();
+        else
+            _context.getContextHandler().handle(load);
+
+        if (_exception.get()!=null)
+        {
+            //if the session could not be restored, take its id out of the pool of currently-in-use
+            //session ids
+            _jdbcSessionIdMgr.removeSession(id);
+            throw _exception.get();
+        }
+
+        return _reference.get();
+    }
+
+    /**
+     * Insert a session into the database.
+     *
+     * @param data
+     * @throws Exception
+     */
+    protected void storeSession (Session session)
+    throws Exception
+    {
+        if (session==null)
+            return;
+
+        //put into the database
+        Connection connection = getConnection();
+        PreparedStatement statement = null;
+        try
+        {
+            String rowId = calculateRowId(session);
+
+            long now = System.currentTimeMillis();
+            connection.setAutoCommit(true);
+            statement = connection.prepareStatement(_jdbcSessionIdMgr._insertSession);
+            statement.setString(1, rowId); //rowId
+            statement.setString(2, session.getId()); //session id
+            statement.setString(3, session.getCanonicalContext()); //context path
+            statement.setString(4, session.getVirtualHost()); //first vhost
+            statement.setString(5, getSessionIdManager().getWorkerName());//my node id
+            statement.setLong(6, session.getAccessed());//accessTime
+            statement.setLong(7, session.getLastAccessedTime()); //lastAccessTime
+            statement.setLong(8, session.getCreationTime()); //time created
+            statement.setLong(9, session.getCookieSet());//time cookie was set
+            statement.setLong(10, now); //last saved time
+            statement.setLong(11, session.getExpiryTime());
+
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            ObjectOutputStream oos = new ObjectOutputStream(baos);
+            oos.writeObject(session.getAttributeMap());
+            byte[] bytes = baos.toByteArray();
+
+            ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
+            statement.setBinaryStream(12, bais, bytes.length);//attribute map as blob
+
+            statement.executeUpdate();
+            session.setRowId(rowId); //set it on the in-memory data as well as in db
+            session.setLastSaved(now);
+
+
+            if (LOG.isDebugEnabled())
+                LOG.debug("Stored session "+session);
+        }
+        finally
+        {
+            if (statement!=null)
+            {
+                try { statement.close(); }
+                catch(Exception e) { LOG.warn(e); }
+            }
+
+            if (connection!=null)
+                connection.close();
+        }
+    }
+
+
+    /**
+     * Update data on an existing persisted session.
+     *
+     * @param data the session
+     * @throws Exception
+     */
+    protected void updateSession (Session data)
+    throws Exception
+    {
+        if (data==null)
+            return;
+
+        Connection connection = getConnection();
+        PreparedStatement statement = null;
+        try
+        {
+            long now = System.currentTimeMillis();
+            connection.setAutoCommit(true);
+            statement = connection.prepareStatement(_jdbcSessionIdMgr._updateSession);
+            statement.setString(1, getSessionIdManager().getWorkerName());//my node id
+            statement.setLong(2, data.getAccessed());//accessTime
+            statement.setLong(3, data.getLastAccessedTime()); //lastAccessTime
+            statement.setLong(4, now); //last saved time
+            statement.setLong(5, data.getExpiryTime());
+
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            ObjectOutputStream oos = new ObjectOutputStream(baos);
+            oos.writeObject(data.getAttributeMap());
+            byte[] bytes = baos.toByteArray();
+            ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
+
+            statement.setBinaryStream(6, bais, bytes.length);//attribute map as blob
+            statement.setString(7, data.getRowId()); //rowId
+            statement.executeUpdate();
+
+            data.setLastSaved(now);
+            if (LOG.isDebugEnabled())
+                LOG.debug("Updated session "+data);
+        }
+        finally
+        {
+            if (statement!=null)
+            {
+                try { statement.close(); }
+                catch(Exception e) { LOG.warn(e); }
+            }
+
+            if (connection!=null)
+                connection.close();
+        }
+    }
+
+
+    /**
+     * Update the node on which the session was last seen to be my node.
+     *
+     * @param data the session
+     * @throws Exception
+     */
+    protected void updateSessionNode (Session data)
+    throws Exception
+    {
+        String nodeId = getSessionIdManager().getWorkerName();
+        Connection connection = getConnection();
+        PreparedStatement statement = null;
+        try
+        {
+            connection.setAutoCommit(true);
+            statement = connection.prepareStatement(_jdbcSessionIdMgr._updateSessionNode);
+            statement.setString(1, nodeId);
+            statement.setString(2, data.getRowId());
+            statement.executeUpdate();
+            statement.close();
+            if (LOG.isDebugEnabled())
+                LOG.debug("Updated last node for session id="+data.getId()+", lastNode = "+nodeId);
+        }
+        finally
+        {
+            if (statement!=null)
+            {
+                try { statement.close(); }
+                catch(Exception e) { LOG.warn(e); }
+            }
+
+            if (connection!=null)
+                connection.close();
+        }
+    }
+
+    /**
+     * Persist the time the session was last accessed.
+     *
+     * @param data the session
+     * @throws Exception
+     */
+    private void updateSessionAccessTime (Session data)
+    throws Exception
+    {
+        Connection connection = getConnection();
+        PreparedStatement statement = null;
+        try
+        {
+            long now = System.currentTimeMillis();
+            connection.setAutoCommit(true);
+            statement = connection.prepareStatement(_jdbcSessionIdMgr._updateSessionAccessTime);
+            statement.setString(1, getSessionIdManager().getWorkerName());
+            statement.setLong(2, data.getAccessed());
+            statement.setLong(3, data.getLastAccessedTime());
+            statement.setLong(4, now);
+            statement.setLong(5, data.getExpiryTime());
+            statement.setString(6, data.getRowId());
+            statement.executeUpdate();
+            data.setLastSaved(now);
+            statement.close();
+            if (LOG.isDebugEnabled())
+                LOG.debug("Updated access time session id="+data.getId());
+        }
+        finally
+        {
+            if (statement!=null)
+            {
+                try { statement.close(); }
+                catch(Exception e) { LOG.warn(e); }
+            }
+
+            if (connection!=null)
+                connection.close();
+        }
+    }
+
+
+
+
+    /**
+     * Delete a session from the database. Should only be called
+     * when the session has been invalidated.
+     *
+     * @param data
+     * @throws Exception
+     */
+    protected void deleteSession (Session data)
+    throws Exception
+    {
+        Connection connection = getConnection();
+        PreparedStatement statement = null;
+        try
+        {
+            connection.setAutoCommit(true);
+            statement = connection.prepareStatement(_jdbcSessionIdMgr._deleteSession);
+            statement.setString(1, data.getRowId());
+            statement.executeUpdate();
+            if (LOG.isDebugEnabled())
+                LOG.debug("Deleted Session "+data);
+        }
+        finally
+        {
+            if (statement!=null)
+            {
+                try { statement.close(); }
+                catch(Exception e) { LOG.warn(e); }
+            }
+
+            if (connection!=null)
+                connection.close();
+        }
+    }
+
+
+
+    /**
+     * Get a connection from the driver.
+     * @return
+     * @throws SQLException
+     */
+    private Connection getConnection ()
+    throws SQLException
+    {
+        return ((JDBCSessionIdManager)getSessionIdManager()).getConnection();
+    }
+
+    /**
+     * Calculate a unique id for this session across the cluster.
+     *
+     * Unique id is composed of: contextpath_virtualhost0_sessionid
+     * @param data
+     * @return
+     */
+    private String calculateRowId (Session data)
+    {
+        String rowId = canonicalize(_context.getContextPath());
+        rowId = rowId + "_" + getVirtualHost(_context);
+        rowId = rowId+"_"+data.getId();
+        return rowId;
+    }
+
+    /**
+     * Get the first virtual host for the context.
+     *
+     * Used to help identify the exact session/contextPath.
+     *
+     * @return 0.0.0.0 if no virtual host is defined
+     */
+    private static String getVirtualHost (ContextHandler.Context context)
+    {
+        String vhost = "0.0.0.0";
+
+        if (context==null)
+            return vhost;
+
+        String [] vhosts = context.getContextHandler().getVirtualHosts();
+        if (vhosts==null || vhosts.length==0 || vhosts[0]==null)
+            return vhost;
+
+        return vhosts[0];
+    }
+
+    /**
+     * Make an acceptable file name from a context path.
+     *
+     * @param path
+     * @return
+     */
+    private static String canonicalize (String path)
+    {
+        if (path==null)
+            return "";
+
+        return path.replace('/', '_').replace('.','_').replace('\\','_');
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/session/SessionHandler.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,346 @@
+//
+//  ========================================================================
+//  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.server.session;
+
+import java.io.IOException;
+import java.util.EnumSet;
+import java.util.EventListener;
+import javax.servlet.DispatcherType;
+import javax.servlet.ServletException;
+import javax.servlet.SessionTrackingMode;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.eclipse.jetty.http.HttpCookie;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.SessionManager;
+import org.eclipse.jetty.server.handler.ScopedHandler;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/* ------------------------------------------------------------ */
+/**
+ * SessionHandler.
+ */
+public class SessionHandler extends ScopedHandler
+{
+    final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
+
+    public final static EnumSet<SessionTrackingMode> DEFAULT_TRACKING = EnumSet.of(SessionTrackingMode.COOKIE,SessionTrackingMode.URL);
+
+    /* -------------------------------------------------------------- */
+    private SessionManager _sessionManager;
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Constructor. Construct a SessionHandler witha a HashSessionManager with a standard java.util.Random generator is created.
+     */
+    public SessionHandler()
+    {
+        this(new HashSessionManager());
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param manager
+     *            The session manager
+     */
+    public SessionHandler(SessionManager manager)
+    {
+        setSessionManager(manager);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the sessionManager.
+     */
+    public SessionManager getSessionManager()
+    {
+        return _sessionManager;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param sessionManager
+     *            The sessionManager to set.
+     */
+    public void setSessionManager(SessionManager sessionManager)
+    {
+        if (isStarted())
+            throw new IllegalStateException();
+        SessionManager old_session_manager = _sessionManager;
+
+        if (getServer() != null)
+            getServer().getContainer().update(this,old_session_manager,sessionManager,"sessionManager",true);
+
+        if (sessionManager != null)
+            sessionManager.setSessionHandler(this);
+
+        _sessionManager = sessionManager;
+
+        if (old_session_manager != null)
+            old_session_manager.setSessionHandler(null);
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void setServer(Server server)
+    {
+        Server old_server = getServer();
+        if (old_server != null && old_server != server)
+            old_server.getContainer().update(this,_sessionManager,null,"sessionManager",true);
+        super.setServer(server);
+        if (server != null && server != old_server)
+            server.getContainer().update(this,null,_sessionManager,"sessionManager",true);
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.thread.AbstractLifeCycle#doStart()
+     */
+    @Override
+    protected void doStart() throws Exception
+    {
+        _sessionManager.start();
+        super.doStart();
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.thread.AbstractLifeCycle#doStop()
+     */
+    @Override
+    protected void doStop() throws Exception
+    {
+        // Destroy sessions before destroying servlets/filters see JETTY-1266
+        _sessionManager.stop();
+        super.doStop();
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.jetty.server.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int)
+     */
+    @Override
+    public void doScope(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+    {
+        SessionManager old_session_manager = null;
+        HttpSession old_session = null;
+        HttpSession access = null;
+        try
+        {
+            old_session_manager = baseRequest.getSessionManager();
+            old_session = baseRequest.getSession(false);
+
+            if (old_session_manager != _sessionManager)
+            {
+                // new session context
+                baseRequest.setSessionManager(_sessionManager);
+                baseRequest.setSession(null);
+                checkRequestedSessionId(baseRequest,request);
+            }
+
+            // access any existing session
+            HttpSession session = null;
+            if (_sessionManager != null)
+            {
+                session = baseRequest.getSession(false);
+                if (session != null)
+                {
+                    if (session != old_session)
+                    {
+                        access = session;
+                        HttpCookie cookie = _sessionManager.access(session,request.isSecure());
+                        if (cookie != null) // Handle changed ID or max-age refresh
+                            baseRequest.getResponse().addCookie(cookie);
+                    }
+                }
+                else
+                {
+                    session = baseRequest.recoverNewSession(_sessionManager);
+                    if (session != null)
+                        baseRequest.setSession(session);
+                }
+            }
+
+            if (LOG.isDebugEnabled())
+            {
+                LOG.debug("sessionManager=" + _sessionManager);
+                LOG.debug("session=" + session);
+            }
+
+            // start manual inline of nextScope(target,baseRequest,request,response);
+            if (_nextScope != null)
+                _nextScope.doScope(target,baseRequest,request,response);
+            else if (_outerScope != null)
+                _outerScope.doHandle(target,baseRequest,request,response);
+            else
+                doHandle(target,baseRequest,request,response);
+            // end manual inline (pathentic attempt to reduce stack depth)
+
+        }
+        finally
+        {
+            if (access != null)
+                _sessionManager.complete(access);
+
+            HttpSession session = baseRequest.getSession(false);
+            if (session != null && old_session == null && session != access)
+                _sessionManager.complete(session);
+
+            if (old_session_manager != null && old_session_manager != _sessionManager)
+            {
+                baseRequest.setSessionManager(old_session_manager);
+                baseRequest.setSession(old_session);
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /*
+     * @see org.eclipse.jetty.server.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int)
+     */
+    @Override
+    public void doHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+    {
+        // start manual inline of nextHandle(target,baseRequest,request,response);
+        if (never())
+            nextHandle(target,baseRequest,request,response);
+        else if (_nextScope != null && _nextScope == _handler)
+            _nextScope.doHandle(target,baseRequest,request,response);
+        else if (_handler != null)
+            _handler.handle(target,baseRequest,request,response);
+        // end manual inline
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Look for a requested session ID in cookies and URI parameters
+     *
+     * @param baseRequest
+     * @param request
+     */
+    protected void checkRequestedSessionId(Request baseRequest, HttpServletRequest request)
+    {
+        String requested_session_id = request.getRequestedSessionId();
+
+        SessionManager sessionManager = getSessionManager();
+
+        if (requested_session_id != null && sessionManager != null)
+        {
+            HttpSession session = sessionManager.getHttpSession(requested_session_id);
+            if (session != null && sessionManager.isValid(session))
+                baseRequest.setSession(session);
+            return;
+        }
+        else if (!DispatcherType.REQUEST.equals(baseRequest.getDispatcherType()))
+            return;
+
+        boolean requested_session_id_from_cookie = false;
+        HttpSession session = null;
+
+        // Look for session id cookie
+        if (_sessionManager.isUsingCookies())
+        {
+            Cookie[] cookies = request.getCookies();
+            if (cookies != null && cookies.length > 0)
+            {
+                final String sessionCookie=sessionManager.getSessionCookieConfig().getName();
+                for (int i = 0; i < cookies.length; i++)
+                {
+                    if (sessionCookie.equalsIgnoreCase(cookies[i].getName()))
+                    {
+                        requested_session_id = cookies[i].getValue();
+                        requested_session_id_from_cookie = true;
+
+                        LOG.debug("Got Session ID {} from cookie",requested_session_id);
+
+                        if (requested_session_id != null)
+                        {
+                            session = sessionManager.getHttpSession(requested_session_id);
+
+                            if (session != null && sessionManager.isValid(session))
+                            {
+                                break;
+                            }
+                        }
+                        else
+                        {
+                            LOG.warn("null session id from cookie");
+                        }
+                    }
+                }
+            }
+        }
+
+        if (requested_session_id == null || session == null)
+        {
+            String uri = request.getRequestURI();
+
+            String prefix = sessionManager.getSessionIdPathParameterNamePrefix();
+            if (prefix != null)
+            {
+                int s = uri.indexOf(prefix);
+                if (s >= 0)
+                {
+                    s += prefix.length();
+                    int i = s;
+                    while (i < uri.length())
+                    {
+                        char c = uri.charAt(i);
+                        if (c == ';' || c == '#' || c == '?' || c == '/')
+                            break;
+                        i++;
+                    }
+
+                    requested_session_id = uri.substring(s,i);
+                    requested_session_id_from_cookie = false;
+                    session = sessionManager.getHttpSession(requested_session_id);
+                    if (LOG.isDebugEnabled())
+                        LOG.debug("Got Session ID {} from URL",requested_session_id);
+                }
+            }
+        }
+
+        baseRequest.setRequestedSessionId(requested_session_id);
+        baseRequest.setRequestedSessionIdFromCookie(requested_session_id != null && requested_session_id_from_cookie);
+        if (session != null && sessionManager.isValid(session))
+            baseRequest.setSession(session);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param listener
+     */
+    public void addEventListener(EventListener listener)
+    {
+        if (_sessionManager != null)
+            _sessionManager.addEventListener(listener);
+    }
+
+    /* ------------------------------------------------------------ */
+    public void clearEventListeners()
+    {
+        if (_sessionManager != null)
+            _sessionManager.clearEventListeners();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/ssl/ServletSSL.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,88 @@
+//
+//  ========================================================================
+//  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.server.ssl;
+
+/* --------------------------------------------------------------------- */
+/**
+ * Jetty Servlet SSL support utilities.
+ * <p>
+ * A collection of utilities required to support the SSL requirements of the Servlet 2.2 and 2.3
+ * specs.
+ * 
+ * <p>
+ * Used by the SSL listener classes.
+ * 
+ * 
+ */
+public class ServletSSL
+{
+    /* ------------------------------------------------------------ */
+    /**
+     * Given the name of a TLS/SSL cipher suite, return an int representing it effective stream
+     * cipher key strength. i.e. How much entropy material is in the key material being fed into the
+     * encryption routines.
+     * 
+     * <p>
+     * This is based on the information on effective key lengths in RFC 2246 - The TLS Protocol
+     * Version 1.0, Appendix C. CipherSuite definitions:
+     * 
+     * <pre>
+     *                         Effective 
+     *     Cipher       Type    Key Bits 
+     * 		       	       
+     *     NULL       * Stream     0     
+     *     IDEA_CBC     Block    128     
+     *     RC2_CBC_40 * Block     40     
+     *     RC4_40     * Stream    40     
+     *     RC4_128      Stream   128     
+     *     DES40_CBC  * Block     40     
+     *     DES_CBC      Block     56     
+     *     3DES_EDE_CBC Block    168     
+     * </pre>
+     * 
+     * @param cipherSuite String name of the TLS cipher suite.
+     * @return int indicating the effective key entropy bit-length.
+     */
+    public static int deduceKeyLength(String cipherSuite)
+    {
+        // Roughly ordered from most common to least common.
+        if (cipherSuite == null)
+            return 0;
+        else if (cipherSuite.indexOf("WITH_AES_256_") >= 0)
+            return 256;
+        else if (cipherSuite.indexOf("WITH_RC4_128_") >= 0)
+            return 128;
+        else if (cipherSuite.indexOf("WITH_AES_128_") >= 0)
+            return 128;
+        else if (cipherSuite.indexOf("WITH_RC4_40_") >= 0)
+            return 40;
+        else if (cipherSuite.indexOf("WITH_3DES_EDE_CBC_") >= 0)
+            return 168;
+        else if (cipherSuite.indexOf("WITH_IDEA_CBC_") >= 0)
+            return 128;
+        else if (cipherSuite.indexOf("WITH_RC2_CBC_40_") >= 0)
+            return 40;
+        else if (cipherSuite.indexOf("WITH_DES40_CBC_") >= 0)
+            return 40;
+        else if (cipherSuite.indexOf("WITH_DES_CBC_") >= 0)
+            return 56;
+        else
+            return 0;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/ssl/SslCertificates.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,182 @@
+//
+//  ========================================================================
+//  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.server.ssl;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+
+import org.eclipse.jetty.http.HttpSchemes;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.bio.SocketEndPoint;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.util.TypeUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+public class SslCertificates
+{
+    private static final Logger LOG = Log.getLogger(SslCertificates.class);
+
+    /**
+     * The name of the SSLSession attribute that will contain any cached information.
+     */
+    static final String CACHED_INFO_ATTR = CachedInfo.class.getName();
+
+    public static X509Certificate[] getCertChain(SSLSession sslSession)
+    {
+        try
+        {
+            javax.security.cert.X509Certificate javaxCerts[]=sslSession.getPeerCertificateChain();
+            if (javaxCerts==null||javaxCerts.length==0)
+                return null;
+
+            int length=javaxCerts.length;
+            X509Certificate[] javaCerts=new X509Certificate[length];
+
+            java.security.cert.CertificateFactory cf=java.security.cert.CertificateFactory.getInstance("X.509");
+            for (int i=0; i<length; i++)
+            {
+                byte bytes[]=javaxCerts[i].getEncoded();
+                ByteArrayInputStream stream=new ByteArrayInputStream(bytes);
+                javaCerts[i]=(X509Certificate)cf.generateCertificate(stream);
+            }
+
+            return javaCerts;
+        }
+        catch (SSLPeerUnverifiedException pue)
+        {
+            return null;
+        }
+        catch (Exception e)
+        {
+            LOG.warn(Log.EXCEPTION,e);
+            return null;
+        }
+    }
+    
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Allow the Listener a chance to customise the request. before the server
+     * does its stuff. <br>
+     * This allows the required attributes to be set for SSL requests. <br>
+     * The requirements of the Servlet specs are:
+     * <ul>
+     * <li> an attribute named "javax.servlet.request.ssl_session_id" of type
+     * String (since Servlet Spec 3.0).</li>
+     * <li> an attribute named "javax.servlet.request.cipher_suite" of type
+     * String.</li>
+     * <li> an attribute named "javax.servlet.request.key_size" of type Integer.</li>
+     * <li> an attribute named "javax.servlet.request.X509Certificate" of type
+     * java.security.cert.X509Certificate[]. This is an array of objects of type
+     * X509Certificate, the order of this array is defined as being in ascending
+     * order of trust. The first certificate in the chain is the one set by the
+     * client, the next is the one used to authenticate the first, and so on.
+     * </li>
+     * </ul>
+     * 
+     * @param endpoint
+     *                The Socket the request arrived on. This should be a
+     *                {@link SocketEndPoint} wrapping a {@link SSLSocket}.
+     * @param request
+     *                HttpRequest to be customised.
+     */
+    public static void customize(SSLSession sslSession, EndPoint endpoint, Request request) throws IOException
+    {
+        request.setScheme(HttpSchemes.HTTPS);
+
+        try
+        {
+            String cipherSuite=sslSession.getCipherSuite();
+            Integer keySize;
+            X509Certificate[] certs;
+            String idStr;
+
+            CachedInfo cachedInfo=(CachedInfo)sslSession.getValue(CACHED_INFO_ATTR);
+            if (cachedInfo!=null)
+            {
+                keySize=cachedInfo.getKeySize();
+                certs=cachedInfo.getCerts();
+                idStr=cachedInfo.getIdStr();
+            }
+            else
+            {
+                keySize=new Integer(ServletSSL.deduceKeyLength(cipherSuite));
+                certs=SslCertificates.getCertChain(sslSession);
+                byte[] bytes = sslSession.getId();
+                idStr = TypeUtil.toHexString(bytes);
+                cachedInfo=new CachedInfo(keySize,certs,idStr);
+                sslSession.putValue(CACHED_INFO_ATTR,cachedInfo);
+            }
+
+            if (certs!=null)
+                request.setAttribute("javax.servlet.request.X509Certificate",certs);
+
+            request.setAttribute("javax.servlet.request.cipher_suite",cipherSuite);
+            request.setAttribute("javax.servlet.request.key_size",keySize);
+            request.setAttribute("javax.servlet.request.ssl_session_id", idStr);
+        }
+        catch (Exception e)
+        {
+            LOG.warn(Log.EXCEPTION,e);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    /**
+     * Simple bundle of information that is cached in the SSLSession. Stores the
+     * effective keySize and the client certificate chain.
+     */
+    private static class CachedInfo
+    {
+        private final X509Certificate[] _certs;
+        private final Integer _keySize;
+        private final String _idStr;
+
+        CachedInfo(Integer keySize, X509Certificate[] certs,String idStr)
+        {
+            this._keySize=keySize;
+            this._certs=certs;
+            this._idStr=idStr;
+        }
+
+        X509Certificate[] getCerts()
+        {
+            return _certs;
+        }
+
+        Integer getKeySize()
+        {
+            return _keySize;
+        }
+        
+        String getIdStr()
+        {
+            return _idStr;
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/ssl/SslConnector.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,348 @@
+//
+//  ========================================================================
+//  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.server.ssl;
+
+import java.io.File;
+import java.security.SecureRandom;
+import java.security.Security;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.TrustManagerFactory;
+
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+
+
+/* ------------------------------------------------------------ */
+/** The interface for SSL connectors and their configuration methods.
+ * 
+ */
+public interface SslConnector extends Connector
+{
+    @Deprecated
+    public static final String DEFAULT_KEYSTORE_ALGORITHM=(Security.getProperty("ssl.KeyManagerFactory.algorithm")==null?"SunX509":Security.getProperty("ssl.KeyManagerFactory.algorithm"));
+    @Deprecated
+    public static final String DEFAULT_TRUSTSTORE_ALGORITHM=(Security.getProperty("ssl.TrustManagerFactory.algorithm")==null?"SunX509":Security.getProperty("ssl.TrustManagerFactory.algorithm"));
+
+    /** Default value for the keystore location path. @deprecated */
+    @Deprecated
+    public static final String DEFAULT_KEYSTORE = System.getProperty("user.home") + File.separator + ".keystore";
+    
+    /** String name of key password property. @deprecated */
+    @Deprecated
+    public static final String KEYPASSWORD_PROPERTY = "org.eclipse.jetty.ssl.keypassword";
+    
+    /** String name of keystore password property. @deprecated */
+    @Deprecated
+    public static final String PASSWORD_PROPERTY = "org.eclipse.jetty.ssl.password";
+    
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @return the instance of SslContextFactory associated with the connector
+     */
+    public SslContextFactory getSslContextFactory();
+        
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The array of Ciphersuite names to exclude from 
+     * {@link SSLEngine#setEnabledCipherSuites(String[])}
+     * @deprecated
+     */
+    @Deprecated
+    public abstract String[] getExcludeCipherSuites();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param cipherSuites The array of Ciphersuite names to exclude from 
+     * {@link SSLEngine#setEnabledCipherSuites(String[])}
+     * @deprecated
+     */
+    @Deprecated
+    public abstract void setExcludeCipherSuites(String[] cipherSuites);
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The array of Ciphersuite names to include in
+     * {@link SSLEngine#setEnabledCipherSuites(String[])}
+     * @deprecated
+     */
+    @Deprecated
+    public abstract String[] getIncludeCipherSuites();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param cipherSuites The array of Ciphersuite names to include in 
+     * {@link SSLEngine#setEnabledCipherSuites(String[])}
+     * @deprecated
+     */
+    @Deprecated
+    public abstract void setIncludeCipherSuites(String[] cipherSuites);
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param password The password for the key store
+     * @deprecated
+     */
+    @Deprecated
+    public abstract void setPassword(String password);
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param password The password for the trust store
+     * @deprecated
+     */
+    @Deprecated
+    public abstract void setTrustPassword(String password);
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param password The password (if any) for the specific key within 
+     * the key store
+     * @deprecated
+     */
+    @Deprecated
+    public abstract void setKeyPassword(String password);
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The SSL protocol (default "TLS") passed to {@link SSLContext#getInstance(String, String)}
+     * @deprecated
+     */
+    @Deprecated
+    public abstract String getProtocol();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param protocol The SSL protocol (default "TLS") passed to {@link SSLContext#getInstance(String, String)}
+     * @deprecated
+     */
+    @Deprecated
+    public abstract void setProtocol(String protocol);
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param keystore The file or URL of the SSL Key store.
+     * @deprecated
+     */
+    @Deprecated
+    public abstract void setKeystore(String keystore);
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The file or URL of the SSL Key store.
+     * @deprecated
+     */
+    @Deprecated
+    public abstract String getKeystore();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The type of the key store (default "JKS")
+     * @deprecated
+     */
+    @Deprecated
+    public abstract String getKeystoreType();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return True if SSL needs client authentication.
+     * @see SSLEngine#getNeedClientAuth()
+     * @deprecated
+     */
+    @Deprecated
+    public abstract boolean getNeedClientAuth();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return True if SSL wants client authentication.
+     * @see SSLEngine#getWantClientAuth()
+     * @deprecated
+     */
+    @Deprecated
+    public abstract boolean getWantClientAuth();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param needClientAuth True if SSL needs client authentication.
+     * @see SSLEngine#getNeedClientAuth()
+     * @deprecated
+     */
+    @Deprecated
+    public abstract void setNeedClientAuth(boolean needClientAuth);
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param wantClientAuth True if SSL wants client authentication.
+     * @see SSLEngine#getWantClientAuth()
+     * @deprecated
+     */
+    @Deprecated
+    public abstract void setWantClientAuth(boolean wantClientAuth);
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param keystoreType The type of the key store (default "JKS")
+     * @deprecated
+     */
+    @Deprecated
+    public abstract void setKeystoreType(String keystoreType);
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The SSL provider name, which if set is passed to 
+     * {@link SSLContext#getInstance(String, String)}
+     * @deprecated
+     */
+    @Deprecated
+    public abstract String getProvider();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The algorithm name, which if set is passed to 
+     * {@link SecureRandom#getInstance(String)} to obtain the {@link SecureRandom}
+     * instance passed to {@link SSLContext#init(javax.net.ssl.KeyManager[], javax.net.ssl.TrustManager[], SecureRandom)}
+     * @deprecated
+     */
+    @Deprecated
+    public abstract String getSecureRandomAlgorithm();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The algorithm name (default "SunX509") used by the {@link KeyManagerFactory}
+     * @deprecated
+     */
+    @Deprecated
+    public abstract String getSslKeyManagerFactoryAlgorithm();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The algorithm name (default "SunX509") used by the {@link TrustManagerFactory}
+     * @deprecated
+     */
+    @Deprecated
+    public abstract String getSslTrustManagerFactoryAlgorithm();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The file name or URL of the trust store location
+     * @deprecated
+     */
+    @Deprecated
+    public abstract String getTruststore();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The type of the trust store (default "JKS")
+     * @deprecated
+     */
+    @Deprecated
+    public abstract String getTruststoreType();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param provider The SSL provider name, which if set is passed to 
+     * {@link SSLContext#getInstance(String, String)}
+     * @deprecated
+     */
+    @Deprecated
+    public abstract void setProvider(String provider);
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param algorithm The algorithm name, which if set is passed to 
+     * {@link SecureRandom#getInstance(String)} to obtain the {@link SecureRandom}
+     * instance passed to {@link SSLContext#init(javax.net.ssl.KeyManager[], javax.net.ssl.TrustManager[], SecureRandom)}
+     * @deprecated
+     */
+    @Deprecated
+    public abstract void setSecureRandomAlgorithm(String algorithm);
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param algorithm The algorithm name (default "SunX509") used by 
+     * the {@link KeyManagerFactory}
+     * @deprecated
+     */
+    @Deprecated
+    public abstract void setSslKeyManagerFactoryAlgorithm(String algorithm);
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param algorithm The algorithm name (default "SunX509") used by the {@link TrustManagerFactory}
+     * @deprecated
+     */
+    @Deprecated
+    public abstract void setSslTrustManagerFactoryAlgorithm(String algorithm);
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param truststore The file name or URL of the trust store location
+     * @deprecated
+     */
+    @Deprecated
+    public abstract void setTruststore(String truststore);
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param truststoreType The type of the trust store (default "JKS")
+     * @deprecated
+     */
+    @Deprecated
+    public abstract void setTruststoreType(String truststoreType);
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param sslContext Set a preconfigured SSLContext
+     * @deprecated
+     */
+    @Deprecated
+    public abstract void setSslContext(SSLContext sslContext);
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The SSLContext
+     * @deprecated
+     */
+    @Deprecated
+    public abstract SSLContext getSslContext();
+    
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return True if SSL re-negotiation is allowed (default false)
+     * @deprecated
+     */
+    @Deprecated
+    public boolean isAllowRenegotiate();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set if SSL re-negotiation is allowed. CVE-2009-3555 discovered
+     * a vulnerability in SSL/TLS with re-negotiation.  If your JVM
+     * does not have CVE-2009-3555 fixed, then re-negotiation should 
+     * not be allowed.
+     * @param allowRenegotiate true if re-negotiation is allowed (default false)
+     * @deprecated
+     */
+    @Deprecated
+    public void setAllowRenegotiate(boolean allowRenegotiate);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/ssl/SslSelectChannelConnector.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,653 @@
+//
+//  ========================================================================
+//  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.server.ssl;
+
+import java.io.IOException;
+import java.nio.channels.SocketChannel;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+
+import org.eclipse.jetty.http.HttpSchemes;
+import org.eclipse.jetty.io.AsyncEndPoint;
+import org.eclipse.jetty.io.Buffers;
+import org.eclipse.jetty.io.Buffers.Type;
+import org.eclipse.jetty.io.BuffersFactory;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.RuntimeIOException;
+import org.eclipse.jetty.io.bio.SocketEndPoint;
+import org.eclipse.jetty.io.nio.AsyncConnection;
+import org.eclipse.jetty.io.nio.SslConnection;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.nio.SelectChannelConnector;
+import org.eclipse.jetty.util.component.AggregateLifeCycle;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+
+/* ------------------------------------------------------------ */
+/**
+ * SslSelectChannelConnector.
+ *
+ * @org.apache.xbean.XBean element="sslConnector" description="Creates an NIO ssl connector"
+ */
+public class SslSelectChannelConnector extends SelectChannelConnector implements SslConnector
+{
+    private final SslContextFactory _sslContextFactory;
+    private Buffers _sslBuffers;
+
+    /* ------------------------------------------------------------ */
+    public SslSelectChannelConnector()
+    {
+        this(new SslContextFactory(SslContextFactory.DEFAULT_KEYSTORE_PATH));
+        setSoLingerTime(30000);
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Construct with explicit SslContextFactory.
+     * The SslContextFactory passed is added via {@link #addBean(Object)} so that 
+     * it's lifecycle may be managed with {@link AggregateLifeCycle}.
+     * @param sslContextFactory
+     */
+    public SslSelectChannelConnector(SslContextFactory sslContextFactory)
+    {
+        _sslContextFactory = sslContextFactory;
+        addBean(_sslContextFactory);
+        setUseDirectBuffers(false);
+        setSoLingerTime(30000);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Allow the Listener a chance to customise the request. before the server
+     * does its stuff. <br>
+     * This allows the required attributes to be set for SSL requests. <br>
+     * The requirements of the Servlet specs are:
+     * <ul>
+     * <li> an attribute named "javax.servlet.request.ssl_session_id" of type
+     * String (since Servlet Spec 3.0).</li>
+     * <li> an attribute named "javax.servlet.request.cipher_suite" of type
+     * String.</li>
+     * <li> an attribute named "javax.servlet.request.key_size" of type Integer.</li>
+     * <li> an attribute named "javax.servlet.request.X509Certificate" of type
+     * java.security.cert.X509Certificate[]. This is an array of objects of type
+     * X509Certificate, the order of this array is defined as being in ascending
+     * order of trust. The first certificate in the chain is the one set by the
+     * client, the next is the one used to authenticate the first, and so on.
+     * </li>
+     * </ul>
+     *
+     * @param endpoint
+     *                The Socket the request arrived on. This should be a
+     *                {@link SocketEndPoint} wrapping a {@link SSLSocket}.
+     * @param request
+     *                HttpRequest to be customised.
+     */
+    @Override
+    public void customize(EndPoint endpoint, Request request) throws IOException
+    {
+        request.setScheme(HttpSchemes.HTTPS);
+        super.customize(endpoint,request);
+
+        SslConnection.SslEndPoint sslEndpoint=(SslConnection.SslEndPoint)endpoint;
+        SSLEngine sslEngine=sslEndpoint.getSslEngine();
+        SSLSession sslSession=sslEngine.getSession();
+
+        SslCertificates.customize(sslSession,endpoint,request);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return True if SSL re-negotiation is allowed (default false)
+     * @deprecated
+     */
+    @Deprecated
+    public boolean isAllowRenegotiate()
+    {
+        return _sslContextFactory.isAllowRenegotiate();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set if SSL re-negotiation is allowed. CVE-2009-3555 discovered
+     * a vulnerability in SSL/TLS with re-negotiation.  If your JVM
+     * does not have CVE-2009-3555 fixed, then re-negotiation should
+     * not be allowed.  CVE-2009-3555 was fixed in Sun java 1.6 with a ban
+     * of renegotiate in u19 and with RFC5746 in u22.
+     * @param allowRenegotiate true if re-negotiation is allowed (default false)
+     * @deprecated
+     */
+    @Deprecated
+    public void setAllowRenegotiate(boolean allowRenegotiate)
+    {
+        _sslContextFactory.setAllowRenegotiate(allowRenegotiate);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#getExcludeCipherSuites()
+     * @deprecated
+     */
+    @Deprecated
+    public String[] getExcludeCipherSuites()
+    {
+        return _sslContextFactory.getExcludeCipherSuites();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#setExcludeCipherSuites(java.lang.String[])
+     * @deprecated
+     */
+    @Deprecated
+    public void setExcludeCipherSuites(String[] cipherSuites)
+    {
+        _sslContextFactory.setExcludeCipherSuites(cipherSuites);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#getExcludeCipherSuites()
+     * @deprecated
+     */
+    @Deprecated
+    public String[] getIncludeCipherSuites()
+    {
+        return _sslContextFactory.getIncludeCipherSuites();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#setExcludeCipherSuites(java.lang.String[])
+     * @deprecated
+     */
+    @Deprecated
+    public void setIncludeCipherSuites(String[] cipherSuites)
+    {
+        _sslContextFactory.setIncludeCipherSuites(cipherSuites);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#setPassword(java.lang.String)
+     * @deprecated
+     */
+    @Deprecated
+    public void setPassword(String password)
+    {
+        _sslContextFactory.setKeyStorePassword(password);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#setTrustPassword(java.lang.String)
+     * @deprecated
+     */
+    @Deprecated
+    public void setTrustPassword(String password)
+    {
+        _sslContextFactory.setTrustStorePassword(password);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#setKeyPassword(java.lang.String)
+     * @deprecated
+     */
+    @Deprecated
+    public void setKeyPassword(String password)
+    {
+        _sslContextFactory.setKeyManagerPassword(password);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Unsupported.
+     *
+     * TODO: we should remove this as it is no longer an overridden method from SslConnector (like it was in the past)
+     * @deprecated
+     */
+    @Deprecated
+    public String getAlgorithm()
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Unsupported.
+     *
+     * TODO: we should remove this as it is no longer an overridden method from SslConnector (like it was in the past)
+     * @deprecated
+     */
+    @Deprecated
+    public void setAlgorithm(String algorithm)
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#getProtocol()
+     * @deprecated
+     */
+    @Deprecated
+    public String getProtocol()
+    {
+        return _sslContextFactory.getProtocol();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#setProtocol(java.lang.String)
+     * @deprecated
+     */
+    @Deprecated
+    public void setProtocol(String protocol)
+    {
+        _sslContextFactory.setProtocol(protocol);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#setKeystore(java.lang.String)
+     * @deprecated
+     */
+    @Deprecated
+    public void setKeystore(String keystore)
+    {
+        _sslContextFactory.setKeyStorePath(keystore);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#getKeystore()
+     * @deprecated
+     */
+    @Deprecated
+    public String getKeystore()
+    {
+        return _sslContextFactory.getKeyStorePath();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#getKeystoreType()
+     * @deprecated
+     */
+    @Deprecated
+    public String getKeystoreType()
+    {
+        return _sslContextFactory.getKeyStoreType();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#getNeedClientAuth()
+     * @deprecated
+     */
+    @Deprecated
+    public boolean getNeedClientAuth()
+    {
+        return _sslContextFactory.getNeedClientAuth();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#getWantClientAuth()
+     * @deprecated
+     */
+    @Deprecated
+    public boolean getWantClientAuth()
+    {
+        return _sslContextFactory.getWantClientAuth();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#setNeedClientAuth(boolean)
+     * @deprecated
+     */
+    @Deprecated
+    public void setNeedClientAuth(boolean needClientAuth)
+    {
+        _sslContextFactory.setNeedClientAuth(needClientAuth);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#setWantClientAuth(boolean)
+     * @deprecated
+     */
+    @Deprecated
+    public void setWantClientAuth(boolean wantClientAuth)
+    {
+        _sslContextFactory.setWantClientAuth(wantClientAuth);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#setKeystoreType(java.lang.String)
+     * @deprecated
+     */
+    @Deprecated
+    public void setKeystoreType(String keystoreType)
+    {
+        _sslContextFactory.setKeyStoreType(keystoreType);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#getProvider()
+     * @deprecated
+     */
+    @Deprecated
+    public String getProvider()
+    {
+        return _sslContextFactory.getProvider();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#getSecureRandomAlgorithm()
+     * @deprecated
+     */
+    @Deprecated
+    public String getSecureRandomAlgorithm()
+    {
+        return _sslContextFactory.getSecureRandomAlgorithm();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#getSslKeyManagerFactoryAlgorithm()
+     * @deprecated
+     */
+    @Deprecated
+    public String getSslKeyManagerFactoryAlgorithm()
+    {
+        return _sslContextFactory.getSslKeyManagerFactoryAlgorithm();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#getSslTrustManagerFactoryAlgorithm()
+     * @deprecated
+     */
+    @Deprecated
+    public String getSslTrustManagerFactoryAlgorithm()
+    {
+        return _sslContextFactory.getTrustManagerFactoryAlgorithm();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#getTruststore()
+     * @deprecated
+     */
+    @Deprecated
+    public String getTruststore()
+    {
+        return _sslContextFactory.getTrustStore();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#getTruststoreType()
+     * @deprecated
+     */
+    @Deprecated
+    public String getTruststoreType()
+    {
+        return _sslContextFactory.getTrustStoreType();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#setProvider(java.lang.String)
+     * @deprecated
+     */
+    @Deprecated
+    public void setProvider(String provider)
+    {
+        _sslContextFactory.setProvider(provider);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#setSecureRandomAlgorithm(java.lang.String)
+     * @deprecated
+     */
+    @Deprecated
+    public void setSecureRandomAlgorithm(String algorithm)
+    {
+        _sslContextFactory.setSecureRandomAlgorithm(algorithm);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#setSslKeyManagerFactoryAlgorithm(java.lang.String)
+     * @deprecated
+     */
+    @Deprecated
+    public void setSslKeyManagerFactoryAlgorithm(String algorithm)
+    {
+        _sslContextFactory.setSslKeyManagerFactoryAlgorithm(algorithm);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#setSslTrustManagerFactoryAlgorithm(java.lang.String)
+     * @deprecated
+     */
+    @Deprecated
+    public void setSslTrustManagerFactoryAlgorithm(String algorithm)
+    {
+        _sslContextFactory.setTrustManagerFactoryAlgorithm(algorithm);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#setTruststore(java.lang.String)
+     * @deprecated
+     */
+    @Deprecated
+    public void setTruststore(String truststore)
+    {
+        _sslContextFactory.setTrustStore(truststore);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#setTruststoreType(java.lang.String)
+     * @deprecated
+     */
+    @Deprecated
+    public void setTruststoreType(String truststoreType)
+    {
+        _sslContextFactory.setTrustStoreType(truststoreType);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#setSslContext(javax.net.ssl.SSLContext)
+     * @deprecated
+     */
+    @Deprecated
+    public void setSslContext(SSLContext sslContext)
+    {
+        _sslContextFactory.setSslContext(sslContext);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#setSslContext(javax.net.ssl.SSLContext)
+     * @deprecated
+     */
+    @Deprecated
+    public SSLContext getSslContext()
+    {
+        return _sslContextFactory.getSslContext();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#getSslContextFactory()
+     */
+    public SslContextFactory getSslContextFactory()
+    {
+        return _sslContextFactory;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * By default, we're confidential, given we speak SSL. But, if we've been
+     * told about an confidential port, and said port is not our port, then
+     * we're not. This allows separation of listeners providing INTEGRAL versus
+     * CONFIDENTIAL constraints, such as one SSL listener configured to require
+     * client certs providing CONFIDENTIAL, whereas another SSL listener not
+     * requiring client certs providing mere INTEGRAL constraints.
+     */
+    @Override
+    public boolean isConfidential(Request request)
+    {
+        final int confidentialPort=getConfidentialPort();
+        return confidentialPort==0||confidentialPort==request.getServerPort();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * By default, we're integral, given we speak SSL. But, if we've been told
+     * about an integral port, and said port is not our port, then we're not.
+     * This allows separation of listeners providing INTEGRAL versus
+     * CONFIDENTIAL constraints, such as one SSL listener configured to require
+     * client certs providing CONFIDENTIAL, whereas another SSL listener not
+     * requiring client certs providing mere INTEGRAL constraints.
+     */
+    @Override
+    public boolean isIntegral(Request request)
+    {
+        final int integralPort=getIntegralPort();
+        return integralPort==0||integralPort==request.getServerPort();
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    @Override
+    protected AsyncConnection newConnection(SocketChannel channel, AsyncEndPoint endpoint)
+    {
+        try
+        {
+            SSLEngine engine = createSSLEngine(channel);
+            SslConnection connection = newSslConnection(endpoint, engine);
+            AsyncConnection delegate = newPlainConnection(channel, connection.getSslEndPoint());
+            connection.getSslEndPoint().setConnection(delegate);
+            connection.setAllowRenegotiate(_sslContextFactory.isAllowRenegotiate());
+            return connection;
+        }
+        catch (IOException e)
+        {
+            throw new RuntimeIOException(e);
+        }
+    }
+
+    protected AsyncConnection newPlainConnection(SocketChannel channel, AsyncEndPoint endPoint)
+    {
+        return super.newConnection(channel, endPoint);
+    }
+
+    protected SslConnection newSslConnection(AsyncEndPoint endpoint, SSLEngine engine)
+    {
+        return new SslConnection(engine, endpoint);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param channel A channel which if passed is used as to extract remote
+     * host and port for the purposes of SSL session caching
+     * @return A SSLEngine for a new or cached SSL Session
+     * @throws IOException if the SSLEngine cannot be created
+     */
+    protected SSLEngine createSSLEngine(SocketChannel channel) throws IOException
+    {
+        SSLEngine engine;
+        if (channel != null)
+        {
+            String peerHost = channel.socket().getInetAddress().getHostAddress();
+            int peerPort = channel.socket().getPort();
+            engine = _sslContextFactory.newSslEngine(peerHost, peerPort);
+        }
+        else
+        {
+            engine = _sslContextFactory.newSslEngine();
+        }
+
+        engine.setUseClientMode(false);
+        return engine;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.nio.SelectChannelConnector#doStart()
+     */
+    @Override
+    protected void doStart() throws Exception
+    {
+        _sslContextFactory.checkKeyStore();
+        _sslContextFactory.start();
+
+        SSLEngine sslEngine = _sslContextFactory.newSslEngine();
+
+        sslEngine.setUseClientMode(false);
+
+        SSLSession sslSession = sslEngine.getSession();
+
+        _sslBuffers = BuffersFactory.newBuffers(
+                getUseDirectBuffers()?Type.DIRECT:Type.INDIRECT,sslSession.getApplicationBufferSize(),
+                getUseDirectBuffers()?Type.DIRECT:Type.INDIRECT,sslSession.getApplicationBufferSize(),
+                getUseDirectBuffers()?Type.DIRECT:Type.INDIRECT,getMaxBuffers()
+        );
+
+        if (getRequestHeaderSize()<sslSession.getApplicationBufferSize())
+            setRequestHeaderSize(sslSession.getApplicationBufferSize());
+        if (getRequestBufferSize()<sslSession.getApplicationBufferSize())
+            setRequestBufferSize(sslSession.getApplicationBufferSize());
+
+        super.doStart();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.nio.SelectChannelConnector#doStop()
+     */
+    @Override
+    protected void doStop() throws Exception
+    {
+        _sslBuffers=null;
+        super.doStop();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return SSL buffers
+     */
+    public Buffers getSslBuffers()
+    {
+        return _sslBuffers;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/server/ssl/SslSocketConnector.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,712 @@
+//
+//  ========================================================================
+//  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.server.ssl;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+import javax.net.ssl.HandshakeCompletedEvent;
+import javax.net.ssl.HandshakeCompletedListener;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+
+import org.eclipse.jetty.http.HttpSchemes;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.RuntimeIOException;
+import org.eclipse.jetty.io.bio.SocketEndPoint;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.bio.SocketConnector;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+
+/* ------------------------------------------------------------ */
+/**
+ * SSL Socket Connector.
+ *
+ * This specialization of SocketConnector is an abstract listener that can be used as the basis for a
+ * specific JSSE listener.
+ *
+ * The original of this class was heavily based on the work from Court Demas, which in turn is
+ * based on the work from Forge Research. Since JSSE, this class has evolved significantly from
+ * that early work.
+ *
+ * @org.apache.xbean.XBean element="sslSocketConnector" description="Creates an ssl socket connector"
+ *
+ *
+ */
+public class SslSocketConnector extends SocketConnector  implements SslConnector
+{
+    private static final Logger LOG = Log.getLogger(SslSocketConnector.class);
+
+    private final SslContextFactory _sslContextFactory;
+    private int _handshakeTimeout = 0; //0 means use maxIdleTime
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Constructor.
+     */
+    public SslSocketConnector()
+    {
+        this(new SslContextFactory(SslContextFactory.DEFAULT_KEYSTORE_PATH));
+        setSoLingerTime(30000);
+    }
+
+    /* ------------------------------------------------------------ */
+    public SslSocketConnector(SslContextFactory sslContextFactory)
+    {
+        _sslContextFactory = sslContextFactory;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return True if SSL re-negotiation is allowed (default false)
+     */
+    public boolean isAllowRenegotiate()
+    {
+        return _sslContextFactory.isAllowRenegotiate();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set if SSL re-negotiation is allowed. CVE-2009-3555 discovered
+     * a vulnerability in SSL/TLS with re-negotiation.  If your JVM
+     * does not have CVE-2009-3555 fixed, then re-negotiation should
+     * not be allowed.
+     * @param allowRenegotiate true if re-negotiation is allowed (default false)
+     */
+    public void setAllowRenegotiate(boolean allowRenegotiate)
+    {
+        _sslContextFactory.setAllowRenegotiate(allowRenegotiate);
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void accept(int acceptorID)
+        throws IOException, InterruptedException
+    {
+        Socket socket = _serverSocket.accept();
+        configure(socket);
+
+        ConnectorEndPoint connection=new SslConnectorEndPoint(socket);
+        connection.dispatch();
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    protected void configure(Socket socket)
+        throws IOException
+    {
+        super.configure(socket);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Allow the Listener a chance to customise the request. before the server does its stuff. <br>
+     * This allows the required attributes to be set for SSL requests. <br>
+     * The requirements of the Servlet specs are:
+     * <ul>
+     * <li> an attribute named "javax.servlet.request.ssl_id" of type String (since Spec 3.0).</li>
+     * <li> an attribute named "javax.servlet.request.cipher_suite" of type String.</li>
+     * <li> an attribute named "javax.servlet.request.key_size" of type Integer.</li>
+     * <li> an attribute named "javax.servlet.request.X509Certificate" of type
+     * java.security.cert.X509Certificate[]. This is an array of objects of type X509Certificate,
+     * the order of this array is defined as being in ascending order of trust. The first
+     * certificate in the chain is the one set by the client, the next is the one used to
+     * authenticate the first, and so on. </li>
+     * </ul>
+     *
+     * @param endpoint The Socket the request arrived on.
+     *        This should be a {@link SocketEndPoint} wrapping a {@link SSLSocket}.
+     * @param request HttpRequest to be customised.
+     */
+    @Override
+    public void customize(EndPoint endpoint, Request request)
+        throws IOException
+    {
+        super.customize(endpoint, request);
+        request.setScheme(HttpSchemes.HTTPS);
+
+        SocketEndPoint socket_end_point = (SocketEndPoint)endpoint;
+        SSLSocket sslSocket = (SSLSocket)socket_end_point.getTransport();
+        SSLSession sslSession = sslSocket.getSession();
+
+        SslCertificates.customize(sslSession,endpoint,request);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#getExcludeCipherSuites()
+     * @deprecated
+     */
+    @Deprecated
+    public String[] getExcludeCipherSuites() {
+        return _sslContextFactory.getExcludeCipherSuites();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#getIncludeCipherSuites()
+     * @deprecated
+     */
+    @Deprecated
+    public String[] getIncludeCipherSuites()
+    {
+        return _sslContextFactory.getIncludeCipherSuites();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#getKeystore()
+     * @deprecated
+     */
+    @Deprecated
+    public String getKeystore()
+    {
+        return _sslContextFactory.getKeyStorePath();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#getKeystoreType()
+     * @deprecated
+     */
+    @Deprecated
+    public String getKeystoreType()
+    {
+        return _sslContextFactory.getKeyStoreType();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#getNeedClientAuth()
+     * @deprecated
+     */
+    @Deprecated
+    public boolean getNeedClientAuth()
+    {
+        return _sslContextFactory.getNeedClientAuth();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#getProtocol()
+     * @deprecated
+     */
+    @Deprecated
+    public String getProtocol()
+    {
+        return _sslContextFactory.getProtocol();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#getProvider()
+     * @deprecated
+     */
+    @Deprecated
+    public String getProvider() {
+	return _sslContextFactory.getProvider();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#getSecureRandomAlgorithm()
+     * @deprecated
+     */
+    @Deprecated
+    public String getSecureRandomAlgorithm()
+    {
+        return _sslContextFactory.getSecureRandomAlgorithm();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#getSslKeyManagerFactoryAlgorithm()
+     * @deprecated
+     */
+    @Deprecated
+    public String getSslKeyManagerFactoryAlgorithm()
+    {
+        return _sslContextFactory.getSslKeyManagerFactoryAlgorithm();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#getSslTrustManagerFactoryAlgorithm()
+     * @deprecated
+     */
+    @Deprecated
+    public String getSslTrustManagerFactoryAlgorithm()
+    {
+        return _sslContextFactory.getTrustManagerFactoryAlgorithm();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#getTruststore()
+     * @deprecated
+     */
+    @Deprecated
+    public String getTruststore()
+    {
+        return _sslContextFactory.getTrustStore();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#getSslContextFactory()
+     */
+//    @Override
+    public SslContextFactory getSslContextFactory()
+    {
+        return _sslContextFactory;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#getTruststoreType()
+     * @deprecated
+     */
+    @Deprecated
+    public String getTruststoreType()
+    {
+        return _sslContextFactory.getTrustStoreType();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#getWantClientAuth()
+     * @deprecated
+     */
+    @Deprecated
+    public boolean getWantClientAuth()
+    {
+        return _sslContextFactory.getWantClientAuth();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * By default, we're confidential, given we speak SSL. But, if we've been told about an
+     * confidential port, and said port is not our port, then we're not. This allows separation of
+     * listeners providing INTEGRAL versus CONFIDENTIAL constraints, such as one SSL listener
+     * configured to require client certs providing CONFIDENTIAL, whereas another SSL listener not
+     * requiring client certs providing mere INTEGRAL constraints.
+     */
+    @Override
+    public boolean isConfidential(Request request)
+    {
+        final int confidentialPort = getConfidentialPort();
+        return confidentialPort == 0 || confidentialPort == request.getServerPort();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * By default, we're integral, given we speak SSL. But, if we've been told about an integral
+     * port, and said port is not our port, then we're not. This allows separation of listeners
+     * providing INTEGRAL versus CONFIDENTIAL constraints, such as one SSL listener configured to
+     * require client certs providing CONFIDENTIAL, whereas another SSL listener not requiring
+     * client certs providing mere INTEGRAL constraints.
+     */
+    @Override
+    public boolean isIntegral(Request request)
+    {
+        final int integralPort = getIntegralPort();
+        return integralPort == 0 || integralPort == request.getServerPort();
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void open() throws IOException
+    {
+        _sslContextFactory.checkKeyStore();
+        try
+        {
+            _sslContextFactory.start();
+        }
+        catch(Exception e)
+        {
+            throw new RuntimeIOException(e);
+        }
+        super.open();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void doStart() throws Exception
+    {
+        _sslContextFactory.checkKeyStore();
+        _sslContextFactory.start();
+
+        super.doStart();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.bio.SocketConnector#doStop()
+     */
+    @Override
+    protected void doStop() throws Exception
+    {
+        _sslContextFactory.stop();
+
+        super.doStop();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param host The host name that this server should listen on
+     * @param port the port that this server should listen on
+     * @param backlog See {@link ServerSocket#bind(java.net.SocketAddress, int)}
+     * @return A new {@link ServerSocket socket object} bound to the supplied address with all other
+     * settings as per the current configuration of this connector.
+     * @see #setWantClientAuth(boolean)
+     * @see #setNeedClientAuth(boolean)
+     * @exception IOException
+     */
+    @Override
+    protected ServerSocket newServerSocket(String host, int port,int backlog) throws IOException
+    {
+       return _sslContextFactory.newSslServerSocket(host,port,backlog);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#setExcludeCipherSuites(java.lang.String[])
+     * @deprecated
+     */
+    @Deprecated
+    public void setExcludeCipherSuites(String[] cipherSuites)
+    {
+        _sslContextFactory.setExcludeCipherSuites(cipherSuites);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#setIncludeCipherSuites(java.lang.String[])
+     * @deprecated
+     */
+    @Deprecated
+    public void setIncludeCipherSuites(String[] cipherSuites)
+    {
+        _sslContextFactory.setIncludeCipherSuites(cipherSuites);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#setKeyPassword(java.lang.String)
+     * @deprecated
+     */
+    @Deprecated
+    public void setKeyPassword(String password)
+    {
+        _sslContextFactory.setKeyManagerPassword(password);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param keystore The resource path to the keystore, or null for built in keystores.
+     * @deprecated
+     */
+    @Deprecated
+    public void setKeystore(String keystore)
+    {
+        _sslContextFactory.setKeyStorePath(keystore);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#setKeystoreType(java.lang.String)
+     * @deprecated
+     */
+    @Deprecated
+    public void setKeystoreType(String keystoreType)
+    {
+        _sslContextFactory.setKeyStoreType(keystoreType);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set the value of the needClientAuth property
+     *
+     * @param needClientAuth true iff we require client certificate authentication.
+     * @deprecated
+     */
+    @Deprecated
+    public void setNeedClientAuth(boolean needClientAuth)
+    {
+        _sslContextFactory.setNeedClientAuth(needClientAuth);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#setPassword(java.lang.String)
+     * @deprecated
+     */
+    @Deprecated
+    public void setPassword(String password)
+    {
+        _sslContextFactory.setKeyStorePassword(password);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#setTrustPassword(java.lang.String)
+     * @deprecated
+     */
+    @Deprecated
+    public void setTrustPassword(String password)
+    {
+        _sslContextFactory.setTrustStorePassword(password);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#setProtocol(java.lang.String)
+     * @deprecated
+     */
+    @Deprecated
+    public void setProtocol(String protocol)
+    {
+        _sslContextFactory.setProtocol(protocol);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#setProvider(java.lang.String)
+     * @deprecated
+     */
+    @Deprecated
+    public void setProvider(String provider) {
+        _sslContextFactory.setProvider(provider);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#setSecureRandomAlgorithm(java.lang.String)
+     * @deprecated
+     */
+    @Deprecated
+    public void setSecureRandomAlgorithm(String algorithm)
+    {
+        _sslContextFactory.setSecureRandomAlgorithm(algorithm);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#setSslKeyManagerFactoryAlgorithm(java.lang.String)
+     * @deprecated
+     */
+    @Deprecated
+    public void setSslKeyManagerFactoryAlgorithm(String algorithm)
+    {
+        _sslContextFactory.setSslKeyManagerFactoryAlgorithm(algorithm);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#setSslTrustManagerFactoryAlgorithm(java.lang.String)
+     * @deprecated
+     */
+    @Deprecated
+    public void setSslTrustManagerFactoryAlgorithm(String algorithm)
+    {
+        _sslContextFactory.setTrustManagerFactoryAlgorithm(algorithm);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#setTruststore(java.lang.String)
+     * @deprecated
+     */
+    @Deprecated
+    public void setTruststore(String truststore)
+    {
+        _sslContextFactory.setTrustStore(truststore);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#setTruststoreType(java.lang.String)
+     * @deprecated
+     */
+    @Deprecated
+    public void setTruststoreType(String truststoreType)
+    {
+        _sslContextFactory.setTrustStoreType(truststoreType);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#setSslContext(javax.net.ssl.SSLContext)
+     * @deprecated
+     */
+    @Deprecated
+    public void setSslContext(SSLContext sslContext)
+    {
+        _sslContextFactory.setSslContext(sslContext);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.eclipse.jetty.server.ssl.SslConnector#setSslContext(javax.net.ssl.SSLContext)
+     * @deprecated
+     */
+    @Deprecated
+    public SSLContext getSslContext()
+    {
+        return _sslContextFactory.getSslContext();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set the value of the _wantClientAuth property. This property is used
+     * internally when opening server sockets.
+     *
+     * @param wantClientAuth true if we want client certificate authentication.
+     * @see SSLServerSocket#setWantClientAuth
+     * @deprecated
+     */
+    @Deprecated
+    public void setWantClientAuth(boolean wantClientAuth)
+    {
+        _sslContextFactory.setWantClientAuth(wantClientAuth);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set the time in milliseconds for so_timeout during ssl handshaking
+     * @param msec a non-zero value will be used to set so_timeout during
+     * ssl handshakes. A zero value means the maxIdleTime is used instead.
+     */
+    public void setHandshakeTimeout (int msec)
+    {
+        _handshakeTimeout = msec;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    public int getHandshakeTimeout ()
+    {
+        return _handshakeTimeout;
+    }
+
+    /* ------------------------------------------------------------ */
+    public class SslConnectorEndPoint extends ConnectorEndPoint
+    {
+        public SslConnectorEndPoint(Socket socket) throws IOException
+        {
+            super(socket);
+        }
+
+        @Override
+        public void shutdownOutput() throws IOException
+        {
+            close();
+        }
+
+        @Override
+        public void shutdownInput() throws IOException
+        {
+            close();
+        }
+
+        @Override
+        public void run()
+        {
+            try
+            {
+                int handshakeTimeout = getHandshakeTimeout();
+                int oldTimeout = _socket.getSoTimeout();
+                if (handshakeTimeout > 0)
+                    _socket.setSoTimeout(handshakeTimeout);
+
+                final SSLSocket ssl=(SSLSocket)_socket;
+                ssl.addHandshakeCompletedListener(new HandshakeCompletedListener()
+                {
+                    boolean handshook=false;
+                    public void handshakeCompleted(HandshakeCompletedEvent event)
+                    {
+                        if (handshook)
+                        {
+                            if (!_sslContextFactory.isAllowRenegotiate())
+                            {
+                                LOG.warn("SSL renegotiate denied: "+ssl);
+                                try{ssl.close();}catch(IOException e){LOG.warn(e);}
+                            }
+                        }
+                        else
+                            handshook=true;
+                    }
+                });
+                ssl.startHandshake();
+
+                if (handshakeTimeout>0)
+                    _socket.setSoTimeout(oldTimeout);
+
+                super.run();
+            }
+            catch (SSLException e)
+            {
+                LOG.debug(e);
+                try{close();}
+                catch(IOException e2){LOG.ignore(e2);}
+            }
+            catch (IOException e)
+            {
+                LOG.debug(e);
+                try{close();}
+                catch(IOException e2){LOG.ignore(e2);}
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Unsupported.
+     *
+     * TODO: we should remove this as it is no longer an overridden method from SslConnector (like it was in the past)
+     * @deprecated
+     */
+    @Deprecated
+    public String getAlgorithm()
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Unsupported.
+     *
+     * TODO: we should remove this as it is no longer an overridden method from SslConnector (like it was in the past)
+     * @deprecated
+     */
+    @Deprecated
+    public void setAlgorithm(String algorithm)
+    {
+        throw new UnsupportedOperationException();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/ArrayQueue.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,379 @@
+//
+//  ========================================================================
+//  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.util;
+
+import java.util.AbstractList;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+
+/* ------------------------------------------------------------ */
+/**
+ * Queue backed by circular array.
+ * <p/>
+ * This partial Queue implementation (also with {@link #remove()} for stack operation)
+ * is backed by a growable circular array.
+ *
+ * @param <E>
+ */
+public class ArrayQueue<E> extends AbstractList<E> implements Queue<E>
+{
+    public static final int DEFAULT_CAPACITY = 64;
+    public static final int DEFAULT_GROWTH = 32;
+
+    protected final Object _lock;
+    protected final int _growCapacity;
+    protected Object[] _elements;
+    protected int _nextE;
+    protected int _nextSlot;
+    protected int _size;
+
+    /* ------------------------------------------------------------ */
+    public ArrayQueue()
+    {
+        this(DEFAULT_CAPACITY, -1);
+    }
+
+    /* ------------------------------------------------------------ */
+    public ArrayQueue(int capacity)
+    {
+        this(capacity, -1);
+    }
+
+    /* ------------------------------------------------------------ */
+    public ArrayQueue(int initCapacity, int growBy)
+    {
+        this(initCapacity, growBy, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    public ArrayQueue(int initCapacity, int growBy, Object lock)
+    {
+        _lock = lock == null ? this : lock;
+        _growCapacity = growBy;
+        _elements = new Object[initCapacity];
+    }
+
+    /* ------------------------------------------------------------ */
+    public int getCapacity()
+    {
+        synchronized (_lock)
+        {
+            return _elements.length;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public boolean add(E e)
+    {
+        if (!offer(e))
+            throw new IllegalStateException("Full");
+        return true;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean offer(E e)
+    {
+        synchronized (_lock)
+        {
+            return enqueue(e);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    private boolean enqueue(E e)
+    {
+        if (_size == _elements.length && !grow())
+            return false;
+
+        _size++;
+        _elements[_nextSlot++] = e;
+        if (_nextSlot == _elements.length)
+            _nextSlot = 0;
+
+        return true;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Add without synchronization or bounds checking
+     *
+     * @param e the element to add
+     * @see #add(Object)
+     */
+    public void addUnsafe(E e)
+    {
+        if (!enqueue(e))
+            throw new IllegalStateException("Full");
+    }
+
+    /* ------------------------------------------------------------ */
+    public E element()
+    {
+        synchronized (_lock)
+        {
+            if (isEmpty())
+                throw new NoSuchElementException();
+            return at(_nextE);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private E at(int index)
+    {
+        return (E)_elements[index];
+    }
+
+    /* ------------------------------------------------------------ */
+    public E peek()
+    {
+        synchronized (_lock)
+        {
+            if (isEmpty())
+                return null;
+            return at(_nextE);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public E poll()
+    {
+        synchronized (_lock)
+        {
+            if (_size == 0)
+                return null;
+            return dequeue();
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    private E dequeue()
+    {
+        E e = at(_nextE);
+        _elements[_nextE] = null;
+        _size--;
+        if (++_nextE == _elements.length)
+            _nextE = 0;
+        return e;
+    }
+
+    /* ------------------------------------------------------------ */
+    public E remove()
+    {
+        synchronized (_lock)
+        {
+            if (_size == 0)
+                throw new NoSuchElementException();
+            return dequeue();
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void clear()
+    {
+        synchronized (_lock)
+        {
+            _size = 0;
+            _nextE = 0;
+            _nextSlot = 0;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public boolean isEmpty()
+    {
+        synchronized (_lock)
+        {
+            return _size == 0;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public int size()
+    {
+        synchronized (_lock)
+        {
+            return _size;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public E get(int index)
+    {
+        synchronized (_lock)
+        {
+            if (index < 0 || index >= _size)
+                throw new IndexOutOfBoundsException("!(" + 0 + "<" + index + "<=" + _size + ")");
+            return getUnsafe(index);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Get without synchronization or bounds checking.
+     *
+     * @param  index index of the element to return
+     * @return the element at the specified index
+     * @see #get(int)
+     */
+    public E getUnsafe(int index)
+    {
+        int i = (_nextE + index) % _elements.length;
+        return at(i);
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public E remove(int index)
+    {
+        synchronized (_lock)
+        {
+            if (index < 0 || index >= _size)
+                throw new IndexOutOfBoundsException("!(" + 0 + "<" + index + "<=" + _size + ")");
+
+            int i = (_nextE + index) % _elements.length;
+            E old = at(i);
+
+            if (i < _nextSlot)
+            {
+                // 0                         _elements.length
+                //       _nextE........._nextSlot
+                System.arraycopy(_elements, i + 1, _elements, i, _nextSlot - i);
+                _nextSlot--;
+                _size--;
+            }
+            else
+            {
+                // 0                         _elements.length
+                // ......_nextSlot   _nextE..........
+                System.arraycopy(_elements, i + 1, _elements, i, _elements.length - i - 1);
+                if (_nextSlot > 0)
+                {
+                    _elements[_elements.length - 1] = _elements[0];
+                    System.arraycopy(_elements, 1, _elements, 0, _nextSlot - 1);
+                    _nextSlot--;
+                }
+                else
+                    _nextSlot = _elements.length - 1;
+
+                _size--;
+            }
+
+            return old;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public E set(int index, E element)
+    {
+        synchronized (_lock)
+        {
+            if (index < 0 || index >= _size)
+                throw new IndexOutOfBoundsException("!(" + 0 + "<" + index + "<=" + _size + ")");
+
+            int i = _nextE + index;
+            if (i >= _elements.length)
+                i -= _elements.length;
+            E old = at(i);
+            _elements[i] = element;
+            return old;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void add(int index, E element)
+    {
+        synchronized (_lock)
+        {
+            if (index < 0 || index > _size)
+                throw new IndexOutOfBoundsException("!(" + 0 + "<" + index + "<=" + _size + ")");
+
+            if (_size == _elements.length && !grow())
+                throw new IllegalStateException("Full");
+
+            if (index == _size)
+            {
+                add(element);
+            }
+            else
+            {
+                int i = _nextE + index;
+                if (i >= _elements.length)
+                    i -= _elements.length;
+
+                _size++;
+                _nextSlot++;
+                if (_nextSlot == _elements.length)
+                    _nextSlot = 0;
+
+                if (i < _nextSlot)
+                {
+                    // 0                         _elements.length
+                    //       _nextE.....i..._nextSlot
+                    // 0                         _elements.length
+                    // ..i..._nextSlot   _nextE..........
+                    System.arraycopy(_elements, i, _elements, i + 1, _nextSlot - i);
+                    _elements[i] = element;
+                }
+                else
+                {
+                    // 0                         _elements.length
+                    // ......_nextSlot   _nextE.....i....
+                    if (_nextSlot > 0)
+                    {
+                        System.arraycopy(_elements, 0, _elements, 1, _nextSlot);
+                        _elements[0] = _elements[_elements.length - 1];
+                    }
+
+                    System.arraycopy(_elements, i, _elements, i + 1, _elements.length - i - 1);
+                    _elements[i] = element;
+                }
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    protected boolean grow()
+    {
+        synchronized (_lock)
+        {
+            if (_growCapacity <= 0)
+                return false;
+
+            Object[] elements = new Object[_elements.length + _growCapacity];
+
+            int split = _elements.length - _nextE;
+            if (split > 0)
+                System.arraycopy(_elements, _nextE, elements, 0, split);
+            if (_nextE != 0)
+                System.arraycopy(_elements, 0, elements, split, _nextSlot);
+
+            _elements = elements;
+            _nextE = 0;
+            _nextSlot = _size;
+            return true;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/Atomics.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,73 @@
+//
+//  ========================================================================
+//  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.util;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+public class Atomics
+{
+    private Atomics()
+    {
+    }
+
+    public static void updateMin(AtomicLong currentMin, long newValue)
+    {
+        long oldValue = currentMin.get();
+        while (newValue < oldValue)
+        {
+            if (currentMin.compareAndSet(oldValue, newValue))
+                break;
+            oldValue = currentMin.get();
+        }
+    }
+
+    public static void updateMax(AtomicLong currentMax, long newValue)
+    {
+        long oldValue = currentMax.get();
+        while (newValue > oldValue)
+        {
+            if (currentMax.compareAndSet(oldValue, newValue))
+                break;
+            oldValue = currentMax.get();
+        }
+    }
+
+    public static void updateMin(AtomicInteger currentMin, int newValue)
+    {
+        int oldValue = currentMin.get();
+        while (newValue < oldValue)
+        {
+            if (currentMin.compareAndSet(oldValue, newValue))
+                break;
+            oldValue = currentMin.get();
+        }
+    }
+
+    public static void updateMax(AtomicInteger currentMax, int newValue)
+    {
+        int oldValue = currentMax.get();
+        while (newValue > oldValue)
+        {
+            if (currentMax.compareAndSet(oldValue, newValue))
+                break;
+            oldValue = currentMax.get();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/Attributes.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,36 @@
+//
+//  ========================================================================
+//  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.util;
+
+import java.util.Enumeration;
+
+/* ------------------------------------------------------------ */
+/** Attributes.
+ * Interface commonly used for storing attributes.
+ * 
+ *
+ */
+public interface Attributes
+{
+    public void removeAttribute(String name);
+    public void setAttribute(String name, Object attribute);
+    public Object getAttribute(String name);
+    public Enumeration<String> getAttributeNames();
+    public void clearAttributes();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/AttributesMap.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,163 @@
+//
+//  ========================================================================
+//  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.util;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/* ------------------------------------------------------------ */
+/** AttributesMap.
+ * 
+ *
+ */
+public class AttributesMap implements Attributes
+{
+    protected final Map<String,Object> _map;
+
+    /* ------------------------------------------------------------ */
+    public AttributesMap()
+    {
+        _map=new HashMap<String,Object>();
+    }
+    
+    /* ------------------------------------------------------------ */
+    public AttributesMap(Map<String,Object> map)
+    {
+        _map=map;
+    }
+
+    /* ------------------------------------------------------------ */
+    public AttributesMap(AttributesMap map)
+    {
+        _map=new HashMap<String,Object>(map._map);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /* 
+     * @see org.eclipse.jetty.util.Attributes#removeAttribute(java.lang.String)
+     */
+    public void removeAttribute(String name)
+    {
+        _map.remove(name);
+    }
+
+    /* ------------------------------------------------------------ */
+    /* 
+     * @see org.eclipse.jetty.util.Attributes#setAttribute(java.lang.String, java.lang.Object)
+     */
+    public void setAttribute(String name, Object attribute)
+    {
+        if (attribute==null)
+            _map.remove(name);
+        else
+            _map.put(name, attribute);
+    }
+
+    /* ------------------------------------------------------------ */
+    /* 
+     * @see org.eclipse.jetty.util.Attributes#getAttribute(java.lang.String)
+     */
+    public Object getAttribute(String name)
+    {
+        return _map.get(name);
+    }
+
+    /* ------------------------------------------------------------ */
+    /* 
+     * @see org.eclipse.jetty.util.Attributes#getAttributeNames()
+     */
+    public Enumeration<String> getAttributeNames()
+    {
+        return Collections.enumeration(_map.keySet());
+    }
+
+    /* ------------------------------------------------------------ */
+    /* 
+     * @see org.eclipse.jetty.util.Attributes#getAttributeNames()
+     */
+    public Set<String> getAttributeNameSet()
+    {
+        return _map.keySet();
+    }
+    
+    /* ------------------------------------------------------------ */
+    public Set<Map.Entry<String, Object>> getAttributeEntrySet()
+    {
+        return _map.entrySet();
+    }
+    
+    /* ------------------------------------------------------------ */
+    /* 
+     * @see org.eclipse.jetty.util.Attributes#getAttributeNames()
+     */
+    public static Enumeration<String> getAttributeNamesCopy(Attributes attrs)
+    {
+        if (attrs instanceof AttributesMap)
+            return Collections.enumeration(((AttributesMap)attrs)._map.keySet());
+        
+        List<String> names = new ArrayList<String>();
+        names.addAll(Collections.list(attrs.getAttributeNames()));
+        return Collections.enumeration(names);
+    }
+
+    /* ------------------------------------------------------------ */
+    /* 
+     * @see org.eclipse.jetty.util.Attributes#clear()
+     */
+    public void clearAttributes()
+    {
+        _map.clear();
+    }
+    
+    /* ------------------------------------------------------------ */
+    public int size()
+    {
+        return _map.size();
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public String toString()
+    {
+        return _map.toString();
+    }
+    
+    /* ------------------------------------------------------------ */
+    public Set<String> keySet()
+    {
+        return _map.keySet();
+    }
+    
+    /* ------------------------------------------------------------ */
+    public void addAll(Attributes attributes)
+    {
+        Enumeration<String> e = attributes.getAttributeNames();
+        while (e.hasMoreElements())
+        {
+            String name=e.nextElement();
+            setAttribute(name,attributes.getAttribute(name));
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/B64Code.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,450 @@
+//
+//  ========================================================================
+//  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.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+
+
+/* ------------------------------------------------------------ */
+/** Fast B64 Encoder/Decoder as described in RFC 1421.
+ * <p>Does not insert or interpret whitespace as described in RFC
+ * 1521. If you require this you must pre/post process your data.
+ * <p> Note that in a web context the usual case is to not want
+ * linebreaks or other white space in the encoded output.
+ * 
+ */
+public class B64Code
+{
+    // ------------------------------------------------------------------
+    static final char __pad='=';
+    static final char[] __rfc1421alphabet=
+            {
+                'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
+                'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
+                'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
+                'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
+            };
+
+    static final byte[] __rfc1421nibbles;
+
+    static
+    {
+        __rfc1421nibbles=new byte[256];
+        for (int i=0;i<256;i++)
+            __rfc1421nibbles[i]=-1;
+        for (byte b=0;b<64;b++)
+            __rfc1421nibbles[(byte)__rfc1421alphabet[b]]=b;
+        __rfc1421nibbles[(byte)__pad]=0;
+    }
+
+    // ------------------------------------------------------------------
+    /**
+     * Base 64 encode as described in RFC 1421.
+     * <p>Does not insert whitespace as described in RFC 1521.
+     * @param s String to encode.
+     * @return String containing the encoded form of the input.
+     */
+    static public String encode(String s)
+    {
+        try
+        {
+            return encode(s,null);
+        }
+        catch (UnsupportedEncodingException e)
+        {
+            throw new IllegalArgumentException(e.toString());
+        }
+    }
+
+    // ------------------------------------------------------------------
+    /**
+     * Base 64 encode as described in RFC 1421.
+     * <p>Does not insert whitespace as described in RFC 1521.
+     * @param s String to encode.
+     * @param charEncoding String representing the name of
+     *        the character encoding of the provided input String.
+     * @return String containing the encoded form of the input.
+     */
+    static public String encode(String s,String charEncoding)
+            throws UnsupportedEncodingException
+    {
+        byte[] bytes;
+        if (charEncoding==null)
+            bytes=s.getBytes(StringUtil.__ISO_8859_1);
+        else
+            bytes=s.getBytes(charEncoding);
+
+        return new String(encode(bytes));
+    }
+    
+    // ------------------------------------------------------------------
+    /**
+     * Fast Base 64 encode as described in RFC 1421.
+     * <p>Does not insert whitespace as described in RFC 1521.
+     * <p> Avoids creating extra copies of the input/output.
+     * @param b byte array to encode.
+     * @return char array containing the encoded form of the input.
+     */
+    static public char[] encode(byte[] b)
+    {
+        if (b==null)
+            return null;
+
+        int bLen=b.length;
+        int cLen=((bLen+2)/3)*4;
+        char c[]=new char[cLen];
+        int ci=0;
+        int bi=0;
+        byte b0, b1, b2;
+        int stop=(bLen/3)*3;
+        while (bi<stop)
+        {
+            b0=b[bi++];
+            b1=b[bi++];
+            b2=b[bi++];
+            c[ci++]=__rfc1421alphabet[(b0>>>2)&0x3f];
+            c[ci++]=__rfc1421alphabet[(b0<<4)&0x3f|(b1>>>4)&0x0f];
+            c[ci++]=__rfc1421alphabet[(b1<<2)&0x3f|(b2>>>6)&0x03];
+            c[ci++]=__rfc1421alphabet[b2&077];
+        }
+
+        if (bLen!=bi)
+        {
+            switch (bLen%3)
+            {
+                case 2:
+                    b0=b[bi++];
+                    b1=b[bi++];
+                    c[ci++]=__rfc1421alphabet[(b0>>>2)&0x3f];
+                    c[ci++]=__rfc1421alphabet[(b0<<4)&0x3f|(b1>>>4)&0x0f];
+                    c[ci++]=__rfc1421alphabet[(b1<<2)&0x3f];
+                    c[ci++]=__pad;
+                    break;
+
+                case 1:
+                    b0=b[bi++];
+                    c[ci++]=__rfc1421alphabet[(b0>>>2)&0x3f];
+                    c[ci++]=__rfc1421alphabet[(b0<<4)&0x3f];
+                    c[ci++]=__pad;
+                    c[ci++]=__pad;
+                    break;
+
+                default:
+                    break;
+            }
+        }
+
+        return c;
+    }
+    
+    // ------------------------------------------------------------------
+    /**
+     * Fast Base 64 encode as described in RFC 1421 and RFC2045
+     * <p>Does not insert whitespace as described in RFC 1521, unless rfc2045 is passed as true.
+     * <p> Avoids creating extra copies of the input/output.
+     * @param b byte array to encode.
+     * @param rfc2045 If true, break lines at 76 characters with CRLF
+     * @return char array containing the encoded form of the input.
+     */
+    static public char[] encode(byte[] b, boolean rfc2045)
+    {
+        if (b==null)
+            return null;
+        if (!rfc2045)
+            return encode(b);
+
+        int bLen=b.length;
+        int cLen=((bLen+2)/3)*4;
+        cLen+=2+2*(cLen/76);
+        char c[]=new char[cLen];
+        int ci=0;
+        int bi=0;
+        byte b0, b1, b2;
+        int stop=(bLen/3)*3;
+        int l=0;
+        while (bi<stop)
+        {
+            b0=b[bi++];
+            b1=b[bi++];
+            b2=b[bi++];
+            c[ci++]=__rfc1421alphabet[(b0>>>2)&0x3f];
+            c[ci++]=__rfc1421alphabet[(b0<<4)&0x3f|(b1>>>4)&0x0f];
+            c[ci++]=__rfc1421alphabet[(b1<<2)&0x3f|(b2>>>6)&0x03];
+            c[ci++]=__rfc1421alphabet[b2&077];
+            l+=4;
+            if (l%76==0)
+            {
+                c[ci++]=13;
+                c[ci++]=10;
+            }
+        }
+
+        if (bLen!=bi)
+        {
+            switch (bLen%3)
+            {
+                case 2:
+                    b0=b[bi++];
+                    b1=b[bi++];
+                    c[ci++]=__rfc1421alphabet[(b0>>>2)&0x3f];
+                    c[ci++]=__rfc1421alphabet[(b0<<4)&0x3f|(b1>>>4)&0x0f];
+                    c[ci++]=__rfc1421alphabet[(b1<<2)&0x3f];
+                    c[ci++]=__pad;
+                    break;
+
+                case 1:
+                    b0=b[bi++];
+                    c[ci++]=__rfc1421alphabet[(b0>>>2)&0x3f];
+                    c[ci++]=__rfc1421alphabet[(b0<<4)&0x3f];
+                    c[ci++]=__pad;
+                    c[ci++]=__pad;
+                    break;
+
+                default:
+                    break;
+            }
+        }
+
+        c[ci++]=13;
+        c[ci++]=10;
+        return c;
+    }
+
+    // ------------------------------------------------------------------
+    /**
+     * Base 64 decode as described in RFC 2045.
+     * <p>Unlike {@link #decode(char[])}, extra whitespace is ignored.
+     * @param encoded String to decode.
+     * @param charEncoding String representing the character encoding
+     *        used to map the decoded bytes into a String.
+     * @return String decoded byte array.
+     * @throws UnsupportedEncodingException if the encoding is not supported
+     * @throws IllegalArgumentException if the input is not a valid
+     *         B64 encoding.
+     */
+    static public String decode(String encoded,String charEncoding)
+            throws UnsupportedEncodingException
+    {
+        byte[] decoded=decode(encoded);
+        if (charEncoding==null)
+            return new String(decoded);
+        return new String(decoded,charEncoding);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Fast Base 64 decode as described in RFC 1421.
+     * 
+     * <p>Unlike other decode methods, this does not attempt to 
+     * cope with extra whitespace as described in RFC 1521/2045.
+     * <p> Avoids creating extra copies of the input/output.
+     * <p> Note this code has been flattened for performance.
+     * @param b char array to decode.
+     * @return byte array containing the decoded form of the input.
+     * @throws IllegalArgumentException if the input is not a valid
+     *         B64 encoding.
+     */
+    static public byte[] decode(char[] b)
+    {
+        if (b==null)
+            return null;
+
+        int bLen=b.length;
+        if (bLen%4!=0)
+            throw new IllegalArgumentException("Input block size is not 4");
+
+        int li=bLen-1;
+        while (li>=0 && b[li]==(byte)__pad)
+            li--;
+
+        if (li<0)
+            return new byte[0];
+
+        // Create result array of exact required size.
+        int rLen=((li+1)*3)/4;
+        byte r[]=new byte[rLen];
+        int ri=0;
+        int bi=0;
+        int stop=(rLen/3)*3;
+        byte b0,b1,b2,b3;
+        try
+        {
+            while (ri<stop)
+            {
+                b0=__rfc1421nibbles[b[bi++]];
+                b1=__rfc1421nibbles[b[bi++]];
+                b2=__rfc1421nibbles[b[bi++]];
+                b3=__rfc1421nibbles[b[bi++]];
+                if (b0<0 || b1<0 || b2<0 || b3<0)
+                    throw new IllegalArgumentException("Not B64 encoded");
+
+                r[ri++]=(byte)(b0<<2|b1>>>4);
+                r[ri++]=(byte)(b1<<4|b2>>>2);
+                r[ri++]=(byte)(b2<<6|b3);
+            }
+
+            if (rLen!=ri)
+            {
+                switch (rLen%3)
+                {
+                    case 2:
+                        b0=__rfc1421nibbles[b[bi++]];
+                        b1=__rfc1421nibbles[b[bi++]];
+                        b2=__rfc1421nibbles[b[bi++]];
+                        if (b0<0 || b1<0 || b2<0)
+                            throw new IllegalArgumentException("Not B64 encoded");
+                        r[ri++]=(byte)(b0<<2|b1>>>4);
+                        r[ri++]=(byte)(b1<<4|b2>>>2);
+                        break;
+
+                    case 1:
+                        b0=__rfc1421nibbles[b[bi++]];
+                        b1=__rfc1421nibbles[b[bi++]];
+                        if (b0<0 || b1<0)
+                            throw new IllegalArgumentException("Not B64 encoded");
+                        r[ri++]=(byte)(b0<<2|b1>>>4);
+                        break;
+
+                    default:
+                        break;
+                }
+            }
+        }
+        catch (IndexOutOfBoundsException e)
+        {
+            throw new IllegalArgumentException("char "+bi
+                    +" was not B64 encoded");
+        }
+
+        return r;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Base 64 decode as described in RFC 2045.
+     * <p>Unlike {@link #decode(char[])}, extra whitespace is ignored.
+     * @param encoded String to decode.
+     * @return byte array containing the decoded form of the input.
+     * @throws IllegalArgumentException if the input is not a valid
+     *         B64 encoding.
+     */
+    static public byte[] decode(String encoded)
+    {
+        if (encoded==null)
+            return null;
+
+        ByteArrayOutputStream bout = new ByteArrayOutputStream(4*encoded.length()/3);        
+        decode(encoded, bout);
+        return bout.toByteArray();
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Base 64 decode as described in RFC 2045.
+     * <p>Unlike {@link #decode(char[])}, extra whitespace is ignored.
+     * @param encoded String to decode.
+     * @param output stream for decoded bytes
+     * @return byte array containing the decoded form of the input.
+     * @throws IllegalArgumentException if the input is not a valid
+     *         B64 encoding.
+     */
+    static public void decode (String encoded, ByteArrayOutputStream bout)
+    {
+        if (encoded==null)
+            return;
+        
+        if (bout == null)
+            throw new IllegalArgumentException("No outputstream for decoded bytes");
+        
+        int ci=0;
+        byte nibbles[] = new byte[4];
+        int s=0;
+  
+        while (ci<encoded.length())
+        {
+            char c=encoded.charAt(ci++);
+
+            if (c==__pad)
+                break;
+            
+            if (Character.isWhitespace(c))
+                continue;
+
+            byte nibble=__rfc1421nibbles[c];
+            if (nibble<0)
+                throw new IllegalArgumentException("Not B64 encoded");
+
+            nibbles[s++]=__rfc1421nibbles[c];
+
+            switch(s)
+            {
+                case 1:
+                    break;
+                case 2:
+                    bout.write(nibbles[0]<<2|nibbles[1]>>>4);
+                    break;
+                case 3:
+                    bout.write(nibbles[1]<<4|nibbles[2]>>>2);
+                    break;
+                case 4:
+                    bout.write(nibbles[2]<<6|nibbles[3]);
+                    s=0;
+                    break;
+            }
+
+        }
+
+        return;
+    }
+    
+    
+    /* ------------------------------------------------------------ */
+    public static void encode(int value,Appendable buf) throws IOException
+    {
+        buf.append(__rfc1421alphabet[0x3f&((0xFC000000&value)>>26)]);
+        buf.append(__rfc1421alphabet[0x3f&((0x03F00000&value)>>20)]);
+        buf.append(__rfc1421alphabet[0x3f&((0x000FC000&value)>>14)]);
+        buf.append(__rfc1421alphabet[0x3f&((0x00003F00&value)>>8)]);
+        buf.append(__rfc1421alphabet[0x3f&((0x000000FC&value)>>2)]);
+        buf.append(__rfc1421alphabet[0x3f&((0x00000003&value)<<4)]);
+        buf.append('=');
+    }
+    
+    /* ------------------------------------------------------------ */
+    public static void encode(long lvalue,Appendable buf) throws IOException
+    {
+        int value=(int)(0xFFFFFFFC&(lvalue>>32));
+        buf.append(__rfc1421alphabet[0x3f&((0xFC000000&value)>>26)]);
+        buf.append(__rfc1421alphabet[0x3f&((0x03F00000&value)>>20)]);
+        buf.append(__rfc1421alphabet[0x3f&((0x000FC000&value)>>14)]);
+        buf.append(__rfc1421alphabet[0x3f&((0x00003F00&value)>>8)]);
+        buf.append(__rfc1421alphabet[0x3f&((0x000000FC&value)>>2)]);
+        
+        buf.append(__rfc1421alphabet[0x3f&((0x00000003&value)<<4) + (0xf&(int)(lvalue>>28))]);
+        
+        value=0x0FFFFFFF&(int)lvalue;
+        buf.append(__rfc1421alphabet[0x3f&((0x0FC00000&value)>>22)]);
+        buf.append(__rfc1421alphabet[0x3f&((0x003F0000&value)>>16)]);
+        buf.append(__rfc1421alphabet[0x3f&((0x0000FC00&value)>>10)]);
+        buf.append(__rfc1421alphabet[0x3f&((0x000003F0&value)>>4)]);
+        buf.append(__rfc1421alphabet[0x3f&((0x0000000F&value)<<2)]);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/BlockingArrayQueue.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,704 @@
+//
+//  ========================================================================
+//  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.util;
+
+import java.util.AbstractList;
+import java.util.Collection;
+import java.util.NoSuchElementException;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+
+/* ------------------------------------------------------------ */
+/** Queue backed by a circular array.
+ * 
+ * This queue is uses  a variant of the two lock queue algorithm to
+ * provide an efficient queue or list backed by a growable circular
+ * array.  This queue also has a partial implementation of 
+ * {@link java.util.concurrent.BlockingQueue}, specifically the {@link #take()} and 
+ * {@link #poll(long, TimeUnit)} methods.  
+ * Unlike {@link java.util.concurrent.ArrayBlockingQueue}, this class is
+ * able to grow and provides a blocking put call.
+ * <p>
+ * The queue has both a capacity (the size of the array currently allocated)
+ * and a limit (the maximum size that may be allocated), which defaults to 
+ * {@link Integer#MAX_VALUE}.
+ * 
+ * @param <E> The element type
+ */
+public class BlockingArrayQueue<E> extends AbstractList<E> implements BlockingQueue<E>
+{
+    public final int DEFAULT_CAPACITY=128;
+    public final int DEFAULT_GROWTH=64;
+    private final int _limit;
+    private final AtomicInteger _size=new AtomicInteger();
+    private final int _growCapacity;
+    
+    private volatile int _capacity;
+    private Object[] _elements;
+    
+    private final ReentrantLock _headLock = new ReentrantLock();
+    private final Condition _notEmpty = _headLock.newCondition();
+    private int _head;
+
+    // spacers created to prevent false sharing between head and tail http://en.wikipedia.org/wiki/False_sharing
+    // TODO verify this has benefits
+    private long _space0;
+    private long _space1;
+    private long _space2;
+    private long _space3;
+    private long _space4;
+    private long _space5;
+    private long _space6;
+    private long _space7;
+    
+    private final ReentrantLock _tailLock = new ReentrantLock();
+    private int _tail;
+    
+
+    /* ------------------------------------------------------------ */
+    /** Create a growing partially blocking Queue
+     * 
+     */
+    public BlockingArrayQueue()
+    {
+        _elements=new Object[DEFAULT_CAPACITY];
+        _growCapacity=DEFAULT_GROWTH;
+        _capacity=_elements.length;
+        _limit=Integer.MAX_VALUE;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Create a fixed size partially blocking Queue
+     * @param limit The initial capacity and the limit.
+     */
+    public BlockingArrayQueue(int limit)
+    {
+        _elements=new Object[limit];
+        _capacity=_elements.length;
+        _growCapacity=-1;
+        _limit=limit;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Create a growing partially blocking Queue.
+     * @param capacity Initial capacity
+     * @param growBy Incremental capacity.
+     */
+    public BlockingArrayQueue(int capacity,int growBy)
+    {
+        _elements=new Object[capacity];
+        _capacity=_elements.length;
+        _growCapacity=growBy;
+        _limit=Integer.MAX_VALUE;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Create a growing limited partially blocking Queue.
+     * @param capacity Initial capacity
+     * @param growBy Incremental capacity.
+     * @param limit maximum capacity.
+     */
+    public BlockingArrayQueue(int capacity,int growBy,int limit)
+    {
+        if (capacity>limit)
+            throw new IllegalArgumentException();
+        
+        _elements=new Object[capacity];
+        _capacity=_elements.length;
+        _growCapacity=growBy;
+        _limit=limit;
+    }
+
+    /* ------------------------------------------------------------ */
+    public int getCapacity()
+    {
+        return _capacity;
+    }
+
+    /* ------------------------------------------------------------ */
+    public int getLimit()
+    {
+        return _limit;
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public boolean add(E e)
+    {
+        return offer(e);
+    }
+    
+    /* ------------------------------------------------------------ */
+    public E element()
+    {
+        E e = peek();
+        if (e==null)
+            throw new NoSuchElementException();
+        return e;
+    }
+    
+    /* ------------------------------------------------------------ */
+    @SuppressWarnings("unchecked")
+    public E peek()
+    {
+        if (_size.get() == 0)
+            return null;
+        
+        E e = null;
+        _headLock.lock(); // Size cannot shrink
+        try 
+        {
+            if (_size.get() > 0) 
+                e = (E)_elements[_head];
+        } 
+        finally 
+        {
+            _headLock.unlock();
+        }
+        
+        return e;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean offer(E e)
+    {
+        if (e == null) 
+            throw new NullPointerException();
+        
+        boolean not_empty=false;
+        _tailLock.lock();  // size cannot grow... only shrink
+        try 
+        {
+            if (_size.get() >= _limit) 
+                return false;
+            
+            // should we expand array?
+            if (_size.get()==_capacity)
+            {
+                _headLock.lock();   // Need to grow array
+                try
+                {
+                    if (!grow())
+                        return false;
+                }
+                finally
+                {
+                    _headLock.unlock();
+                }
+            }
+
+            // add the element
+            _elements[_tail]=e;
+            _tail=(_tail+1)%_capacity;
+
+            not_empty=0==_size.getAndIncrement();
+            
+        } 
+        finally 
+        {
+            _tailLock.unlock();
+        }
+        
+        if (not_empty)
+        {
+            _headLock.lock();
+            try
+            {
+                _notEmpty.signal();
+            }
+            finally
+            {
+                _headLock.unlock();
+            }
+        }  
+
+        return true;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    @SuppressWarnings("unchecked")
+    public E poll()
+    {
+        if (_size.get() == 0)
+            return null;
+        
+        E e = null;
+        _headLock.lock(); // Size cannot shrink
+        try 
+        {
+            if (_size.get() > 0) 
+            {
+                final int head=_head;
+                e = (E)_elements[head];
+                _elements[head]=null;
+                _head=(head+1)%_capacity;
+                
+                if (_size.decrementAndGet()>0)
+                    _notEmpty.signal();
+            }
+        } 
+        finally 
+        {
+            _headLock.unlock();
+        }
+        
+        return e;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Retrieves and removes the head of this queue, waiting
+     * if no elements are present on this queue.
+     * @return the head of this queue
+     * @throws InterruptedException if interrupted while waiting.
+     */
+    @SuppressWarnings("unchecked")
+    public E take() throws InterruptedException
+    {
+        E e = null;
+        _headLock.lockInterruptibly();  // Size cannot shrink
+        try 
+        {
+            try 
+            {
+                while (_size.get() == 0)
+                {
+                    _notEmpty.await();
+                }
+            } 
+            catch (InterruptedException ie) 
+            {
+                _notEmpty.signal();
+                throw ie;
+            }
+
+            final int head=_head;
+            e = (E)_elements[head];
+            _elements[head]=null;
+            _head=(head+1)%_capacity;
+
+            if (_size.decrementAndGet()>0)
+                _notEmpty.signal();
+        } 
+        finally 
+        {
+            _headLock.unlock();
+        }
+        
+        return e;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Retrieves and removes the head of this queue, waiting
+     * if necessary up to the specified wait time if no elements are
+     * present on this queue.
+     * @param time how long to wait before giving up, in units of
+     * <tt>unit</tt>
+     * @param unit a <tt>TimeUnit</tt> determining how to interpret the
+     * <tt>timeout</tt> parameter
+     * @return the head of this queue, or <tt>null</tt> if the
+     * specified waiting time elapses before an element is present.
+     * @throws InterruptedException if interrupted while waiting.
+     */
+    @SuppressWarnings("unchecked")
+    public E poll(long time, TimeUnit unit) throws InterruptedException
+    {
+        
+        E e = null;
+
+        long nanos = unit.toNanos(time);
+        
+        _headLock.lockInterruptibly(); // Size cannot shrink
+        try 
+        {    
+            try 
+            {
+                while (_size.get() == 0)
+                {
+                    if (nanos<=0)
+                        return null;
+                    nanos = _notEmpty.awaitNanos(nanos);
+                }
+            } 
+            catch (InterruptedException ie) 
+            {
+                _notEmpty.signal();
+                throw ie;
+            }
+
+            e = (E)_elements[_head];
+            _elements[_head]=null;
+            _head=(_head+1)%_capacity;
+
+            if (_size.decrementAndGet()>0)
+                _notEmpty.signal();
+        } 
+        finally 
+        {
+            _headLock.unlock();
+        }
+        
+        return e;
+    }
+
+    /* ------------------------------------------------------------ */
+    public E remove()
+    {
+        E e=poll();
+        if (e==null)
+            throw new NoSuchElementException();
+        return e;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void clear()
+    {
+        _tailLock.lock();
+        try
+        {
+            _headLock.lock();
+            try
+            {
+                _head=0;
+                _tail=0;
+                _size.set(0);
+            }
+            finally
+            {
+                _headLock.unlock();
+            }
+        }
+        finally
+        {
+            _tailLock.unlock();
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public boolean isEmpty()
+    {
+        return _size.get()==0;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public int size()
+    {
+        return _size.get();
+    }
+
+    /* ------------------------------------------------------------ */
+    @SuppressWarnings("unchecked")
+    @Override
+    public E get(int index)
+    {
+        _tailLock.lock();
+        try
+        {
+            _headLock.lock();
+            try
+            {
+                if (index<0 || index>=_size.get())
+                    throw new IndexOutOfBoundsException("!("+0+"<"+index+"<="+_size+")");
+                int i = _head+index;
+                if (i>=_capacity)
+                    i-=_capacity;
+                return (E)_elements[i];
+            }
+            finally
+            {
+                _headLock.unlock();
+            }
+        }
+        finally
+        {
+            _tailLock.unlock();
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public E remove(int index)
+    {
+        _tailLock.lock();
+        try
+        {
+            _headLock.lock();
+            try
+            {
+
+                if (index<0 || index>=_size.get())
+                    throw new IndexOutOfBoundsException("!("+0+"<"+index+"<="+_size+")");
+
+                int i = _head+index;
+                if (i>=_capacity)
+                    i-=_capacity;
+                @SuppressWarnings("unchecked")
+                E old=(E)_elements[i];
+
+                if (i<_tail)
+                {
+                    System.arraycopy(_elements,i+1,_elements,i,_tail-i);
+                    _tail--;
+                    _size.decrementAndGet();
+                }
+                else
+                {
+                    System.arraycopy(_elements,i+1,_elements,i,_capacity-i-1);
+                    if (_tail>0)
+                    {
+                        _elements[_capacity]=_elements[0];
+                        System.arraycopy(_elements,1,_elements,0,_tail-1);
+                        _tail--;
+                    }
+                    else
+                        _tail=_capacity-1;
+
+                    _size.decrementAndGet();
+                }
+
+                return old;
+            }
+            finally
+            {
+                _headLock.unlock();
+            }
+        }
+        finally
+        {
+            _tailLock.unlock();
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public E set(int index, E e)
+    {
+        if (e == null) 
+            throw new NullPointerException();
+
+        _tailLock.lock();
+        try
+        {
+            _headLock.lock();
+            try
+            {
+
+                if (index<0 || index>=_size.get())
+                    throw new IndexOutOfBoundsException("!("+0+"<"+index+"<="+_size+")");
+
+                int i = _head+index;
+                if (i>=_capacity)
+                    i-=_capacity;
+                @SuppressWarnings("unchecked")
+                E old=(E)_elements[i];
+                _elements[i]=e;
+                return old;
+            }
+            finally
+            {
+                _headLock.unlock();
+            }
+        }
+        finally
+        {
+            _tailLock.unlock();
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public void add(int index, E e)
+    {
+        if (e == null) 
+            throw new NullPointerException();
+
+        _tailLock.lock();
+        try
+        {
+            _headLock.lock();
+            try
+            {
+
+                if (index<0 || index>_size.get())
+                    throw new IndexOutOfBoundsException("!("+0+"<"+index+"<="+_size+")");
+
+                if (index==_size.get())
+                {
+                    add(e);
+                }
+                else
+                {
+                    if (_tail==_head)
+                        if (!grow())
+                            throw new IllegalStateException("full");
+
+                    int i = _head+index;
+                    if (i>=_capacity)
+                        i-=_capacity;
+
+                    _size.incrementAndGet();
+                    _tail=(_tail+1)%_capacity;
+
+
+                    if (i<_tail)
+                    {
+                        System.arraycopy(_elements,i,_elements,i+1,_tail-i);
+                        _elements[i]=e;
+                    }
+                    else
+                    {
+                        if (_tail>0)
+                        {
+                            System.arraycopy(_elements,0,_elements,1,_tail);
+                            _elements[0]=_elements[_capacity-1];
+                        }
+
+                        System.arraycopy(_elements,i,_elements,i+1,_capacity-i-1);
+                        _elements[i]=e;
+                    }
+                }
+            }
+            finally
+            {
+                _headLock.unlock();
+            }
+        }
+        finally
+        {
+            _tailLock.unlock();
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    private boolean grow()
+    {
+        if (_growCapacity<=0)
+            return false;
+
+        _tailLock.lock();
+        try
+        {
+            _headLock.lock();
+            try
+            {
+                final int head=_head;
+                final int tail=_tail;
+                final int new_tail;
+
+                Object[] elements=new Object[_capacity+_growCapacity];
+
+                if (head<tail)
+                {
+                    new_tail=tail-head;
+                    System.arraycopy(_elements,head,elements,0,new_tail);
+                }
+                else if (head>tail || _size.get()>0)
+                {
+                    new_tail=_capacity+tail-head;
+                    int cut=_capacity-head;
+                    System.arraycopy(_elements,head,elements,0,cut);
+                    System.arraycopy(_elements,0,elements,cut,tail);
+                }
+                else
+                {
+                    new_tail=0;
+                }
+
+                _elements=elements;
+                _capacity=_elements.length;
+                _head=0;
+                _tail=new_tail; 
+                return true;
+            }
+            finally
+            {
+                _headLock.unlock();
+            }
+        }
+        finally
+        {
+            _tailLock.unlock();
+        }
+
+    }
+
+    /* ------------------------------------------------------------ */
+    public int drainTo(Collection<? super E> c)
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    /* ------------------------------------------------------------ */
+    public int drainTo(Collection<? super E> c, int maxElements)
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean offer(E o, long timeout, TimeUnit unit) throws InterruptedException
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    /* ------------------------------------------------------------ */
+    public void put(E o) throws InterruptedException
+    {
+        if (!add(o))
+            throw new IllegalStateException("full");
+    }
+
+    /* ------------------------------------------------------------ */
+    public int remainingCapacity()
+    {
+        _tailLock.lock();
+        try
+        {
+            _headLock.lock();
+            try
+            {
+                return getCapacity()-size();
+            }
+            finally
+            {
+                _headLock.unlock();
+            }
+        }
+        finally
+        {
+            _tailLock.unlock();
+        }
+    }
+    
+
+    /* ------------------------------------------------------------ */
+    long sumOfSpace()
+    {
+        // this method exists to stop clever optimisers removing the spacers
+        return _space0++ +_space1++ +_space2++ +_space3++ +_space4++ +_space5++ +_space6++ +_space7++; 
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/ByteArrayISO8859Writer.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,272 @@
+//
+//  ========================================================================
+//  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.util;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+
+/* ------------------------------------------------------------ */
+/** Byte Array ISO 8859 writer. 
+ * This class combines the features of a OutputStreamWriter for
+ * ISO8859 encoding with that of a ByteArrayOutputStream.  It avoids
+ * many inefficiencies associated with these standard library classes.
+ * It has been optimized for standard ASCII characters.
+ * 
+ * 
+ */
+public class ByteArrayISO8859Writer extends Writer
+{
+    private byte[] _buf;
+    private int _size;
+    private ByteArrayOutputStream2 _bout=null;
+    private OutputStreamWriter _writer=null;
+    private boolean _fixed=false;
+
+    /* ------------------------------------------------------------ */
+    /** Constructor. 
+     */
+    public ByteArrayISO8859Writer()
+    {
+        _buf=new byte[2048];
+    } 
+    
+    /* ------------------------------------------------------------ */
+    /** Constructor. 
+     * @param capacity Buffer capacity
+     */
+    public ByteArrayISO8859Writer(int capacity)
+    {
+        _buf=new byte[capacity];
+    }
+    
+    /* ------------------------------------------------------------ */
+    public ByteArrayISO8859Writer(byte[] buf)
+    {
+        _buf=buf;
+        _fixed=true;
+    }
+
+    /* ------------------------------------------------------------ */
+    public Object getLock()
+    {
+        return lock;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public int size()
+    {
+        return _size;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public int capacity()
+    {
+        return _buf.length;
+    }
+
+    /* ------------------------------------------------------------ */
+    public int spareCapacity()
+    {
+        return _buf.length-_size;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public void setLength(int l)
+    {
+        _size=l;
+    }
+
+    /* ------------------------------------------------------------ */
+    public byte[] getBuf()
+    {
+        return _buf;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public void writeTo(OutputStream out)
+        throws IOException
+    {
+        out.write(_buf,0,_size);
+    }
+
+    /* ------------------------------------------------------------ */
+    public void write(char c)
+        throws IOException
+    {
+        ensureSpareCapacity(1);
+        if (c>=0&&c<=0x7f)
+            _buf[_size++]=(byte)c;
+        else
+        {
+            char[] ca ={c};
+            writeEncoded(ca,0,1);
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public void write(char[] ca)
+        throws IOException
+    {
+        ensureSpareCapacity(ca.length);
+        for (int i=0;i<ca.length;i++)
+        {
+            char c=ca[i];
+            if (c>=0&&c<=0x7f)
+                _buf[_size++]=(byte)c;
+            else
+            {
+                writeEncoded(ca,i,ca.length-i);
+                break;
+            }
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public void write(char[] ca,int offset, int length)
+        throws IOException
+    {
+        ensureSpareCapacity(length);
+        for (int i=0;i<length;i++)
+        {
+            char c=ca[offset+i];
+            if (c>=0&&c<=0x7f)
+                _buf[_size++]=(byte)c;
+            else
+            {
+                writeEncoded(ca,offset+i,length-i);
+                break;
+            }
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public void write(String s)
+        throws IOException
+    {
+        if (s==null)
+        {
+            write("null",0,4);
+            return;
+        }
+        
+        int length=s.length();
+        ensureSpareCapacity(length);
+        for (int i=0;i<length;i++)
+        {
+            char c=s.charAt(i);
+            if (c>=0x0&&c<=0x7f)
+                _buf[_size++]=(byte)c;
+            else
+            {
+                writeEncoded(s.toCharArray(),i,length-i);
+                break;
+            }
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public void write(String s,int offset, int length)
+        throws IOException
+    {
+        ensureSpareCapacity(length);
+        for (int i=0;i<length;i++)
+        {
+            char c=s.charAt(offset+i);
+            if (c>=0&&c<=0x7f)
+                _buf[_size++]=(byte)c;
+            else
+            {
+                writeEncoded(s.toCharArray(),offset+i,length-i);
+                break;
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    private void writeEncoded(char[] ca,int offset, int length)
+        throws IOException
+    {
+        if (_bout==null)
+        {
+            _bout = new ByteArrayOutputStream2(2*length);
+            _writer = new OutputStreamWriter(_bout,StringUtil.__ISO_8859_1);
+        }
+        else
+            _bout.reset();
+        _writer.write(ca,offset,length);
+        _writer.flush();
+        ensureSpareCapacity(_bout.getCount());
+        System.arraycopy(_bout.getBuf(),0,_buf,_size,_bout.getCount());
+        _size+=_bout.getCount();
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public void flush()
+    {}
+
+    /* ------------------------------------------------------------ */
+    public void resetWriter()
+    {
+        _size=0;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void close()
+    {}
+
+    /* ------------------------------------------------------------ */
+    public void destroy()
+    {
+        _buf=null;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public void ensureSpareCapacity(int n)
+        throws IOException
+    {
+        if (_size+n>_buf.length)
+        {
+            if (_fixed)
+                throw new IOException("Buffer overflow: "+_buf.length);
+            byte[] buf = new byte[(_buf.length+n)*4/3];
+            System.arraycopy(_buf,0,buf,0,_size);
+            _buf=buf;
+        }
+    }
+
+
+    /* ------------------------------------------------------------ */
+    public byte[] getByteArray()
+    {
+        byte[] data=new byte[_size];
+        System.arraycopy(_buf,0,data,0,_size);
+        return data;
+    }
+    
+}
+    
+    
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/ByteArrayOutputStream2.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,49 @@
+//
+//  ========================================================================
+//  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.util;
+import java.io.ByteArrayOutputStream;
+
+/* ------------------------------------------------------------ */
+/** ByteArrayOutputStream with public internals
+
+ * 
+ */
+public class ByteArrayOutputStream2 extends ByteArrayOutputStream
+{
+    public ByteArrayOutputStream2(){super();}
+    public ByteArrayOutputStream2(int size){super(size);}
+    public byte[] getBuf(){return buf;}
+    public int getCount(){return count;}
+    public void setCount(int count){this.count = count;}
+
+    public void reset(int minSize)
+    {
+        reset();
+        if (buf.length<minSize)
+        {
+            buf=new byte[minSize];
+        }
+    }
+    
+    public void writeUnchecked(int b)
+    {
+        buf[count++]=(byte)b;
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/ConcurrentHashSet.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,126 @@
+//
+//  ========================================================================
+//  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.util;
+
+import java.util.AbstractSet;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class ConcurrentHashSet<E> extends AbstractSet<E> implements Set<E>
+{
+    private final Map<E, Boolean> _map = new ConcurrentHashMap<E, Boolean>();
+    private transient Set<E> _keys = _map.keySet();
+
+    public ConcurrentHashSet()
+    {
+    }
+
+    @Override
+    public boolean add(E e)
+    {
+        return _map.put(e,Boolean.TRUE) == null;
+    }
+
+    @Override
+    public void clear()
+    {
+        _map.clear();
+    }
+
+    @Override
+    public boolean contains(Object o)
+    {
+        return _map.containsKey(o);
+    }
+
+    @Override
+    public boolean containsAll(Collection<?> c)
+    {
+        return _keys.containsAll(c);
+    }
+
+    @Override
+    public boolean equals(Object o)
+    {
+        return o == this || _keys.equals(o);
+    }
+
+    @Override
+    public int hashCode()
+    {
+        return _keys.hashCode();
+    }
+
+    @Override
+    public boolean isEmpty()
+    {
+        return _map.isEmpty();
+    }
+
+    @Override
+    public Iterator<E> iterator()
+    {
+        return _keys.iterator();
+    }
+
+    @Override
+    public boolean remove(Object o)
+    {
+        return _map.remove(o) != null;
+    }
+
+    @Override
+    public boolean removeAll(Collection<?> c)
+    {
+        return _keys.removeAll(c);
+    }
+
+    @Override
+    public boolean retainAll(Collection<?> c)
+    {
+        return _keys.retainAll(c);
+    }
+
+    @Override
+    public int size()
+    {
+        return _map.size();
+    }
+
+    @Override
+    public Object[] toArray()
+    {
+        return _keys.toArray();
+    }
+
+    @Override
+    public <T> T[] toArray(T[] a)
+    {
+        return _keys.toArray(a);
+    }
+
+    @Override
+    public String toString()
+    {
+        return _keys.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/DateCache.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,311 @@
+//
+//  ========================================================================
+//  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.util;
+
+import java.text.DateFormatSymbols;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+/* ------------------------------------------------------------ */
+/**  Date Format Cache.
+ * Computes String representations of Dates and caches
+ * the results so that subsequent requests within the same minute
+ * will be fast.
+ *
+ * Only format strings that contain either "ss" or "ss.SSS" are
+ * handled.
+ *
+ * The timezone of the date may be included as an ID with the "zzz"
+ * format string or as an offset with the "ZZZ" format string.
+ *
+ * If consecutive calls are frequently very different, then this
+ * may be a little slower than a normal DateFormat.
+ *
+ * 
+ * 
+ */
+
+public class DateCache  
+{
+    public static String DEFAULT_FORMAT="EEE MMM dd HH:mm:ss zzz yyyy";
+    private static long __hitWindow=60*60;
+    
+    private String _formatString;
+    private String _tzFormatString;
+    private SimpleDateFormat _tzFormat;
+    
+    private String _minFormatString;
+    private SimpleDateFormat _minFormat;
+
+    private String _secFormatString;
+    private String _secFormatString0;
+    private String _secFormatString1;
+
+    private long _lastMinutes = -1;
+    private long _lastSeconds = -1;
+    private int _lastMs = -1;
+    private String _lastResult = null;
+
+    private Locale _locale	= null;
+    private DateFormatSymbols	_dfs	= null;
+
+    /* ------------------------------------------------------------ */
+    /** Constructor.
+     * Make a DateCache that will use a default format. The default format
+     * generates the same results as Date.toString().
+     */
+    public DateCache()
+    {
+        this(DEFAULT_FORMAT);
+        getFormat().setTimeZone(TimeZone.getDefault());
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Constructor.
+     * Make a DateCache that will use the given format
+     */
+    public DateCache(String format)
+    {
+        _formatString=format;
+        setTimeZone(TimeZone.getDefault());
+        
+    }
+    
+    /* ------------------------------------------------------------ */
+    public DateCache(String format,Locale l)
+    {
+        _formatString=format;
+        _locale = l;
+        setTimeZone(TimeZone.getDefault());       
+    }
+    
+    /* ------------------------------------------------------------ */
+    public DateCache(String format,DateFormatSymbols s)
+    {
+        _formatString=format;
+        _dfs = s;
+        setTimeZone(TimeZone.getDefault());
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set the timezone.
+     * @param tz TimeZone
+     */
+    public synchronized void setTimeZone(TimeZone tz)
+    {
+        setTzFormatString(tz);        
+        if( _locale != null ) 
+        {
+            _tzFormat=new SimpleDateFormat(_tzFormatString,_locale);
+            _minFormat=new SimpleDateFormat(_minFormatString,_locale);
+        }
+        else if( _dfs != null ) 
+        {
+            _tzFormat=new SimpleDateFormat(_tzFormatString,_dfs);
+            _minFormat=new SimpleDateFormat(_minFormatString,_dfs);
+        }
+        else 
+        {
+            _tzFormat=new SimpleDateFormat(_tzFormatString);
+            _minFormat=new SimpleDateFormat(_minFormatString);
+        }
+        _tzFormat.setTimeZone(tz);
+        _minFormat.setTimeZone(tz);
+        _lastSeconds=-1;
+        _lastMinutes=-1;        
+    }
+
+    /* ------------------------------------------------------------ */
+    public TimeZone getTimeZone()
+    {
+        return _tzFormat.getTimeZone();
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Set the timezone.
+     * @param timeZoneId TimeZoneId the ID of the zone as used by
+     * TimeZone.getTimeZone(id)
+     */
+    public void setTimeZoneID(String timeZoneId)
+    {
+        setTimeZone(TimeZone.getTimeZone(timeZoneId));
+    }
+    
+    /* ------------------------------------------------------------ */
+    private synchronized void setTzFormatString(final  TimeZone tz )
+    {
+        int zIndex = _formatString.indexOf( "ZZZ" );
+        if( zIndex >= 0 )
+        {
+            String ss1 = _formatString.substring( 0, zIndex );
+            String ss2 = _formatString.substring( zIndex+3 );
+            int tzOffset = tz.getRawOffset();
+            
+            StringBuilder sb = new StringBuilder(_formatString.length()+10);
+            sb.append(ss1);
+            sb.append("'");
+            if( tzOffset >= 0 )
+                sb.append( '+' );
+            else
+            {
+                tzOffset = -tzOffset;
+                sb.append( '-' );
+            }
+            
+            int raw = tzOffset / (1000*60);		// Convert to seconds
+            int hr = raw / 60;
+            int min = raw % 60;
+            
+            if( hr < 10 )
+                sb.append( '0' );
+            sb.append( hr );
+            if( min < 10 )
+                sb.append( '0' );
+            sb.append( min );
+            sb.append( '\'' );
+            
+            sb.append(ss2);
+            _tzFormatString=sb.toString();            
+        }
+        else
+            _tzFormatString=_formatString;
+        setMinFormatString();
+    }
+
+    
+    /* ------------------------------------------------------------ */
+    private void setMinFormatString()
+    {
+        int i = _tzFormatString.indexOf("ss.SSS");
+        int l = 6;
+        if (i>=0)
+            throw new IllegalStateException("ms not supported");
+        i = _tzFormatString.indexOf("ss");
+        l=2;
+        
+        // Build a formatter that formats a second format string
+        String ss1=_tzFormatString.substring(0,i);
+        String ss2=_tzFormatString.substring(i+l);
+        _minFormatString =ss1+"'ss'"+ss2;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Format a date according to our stored formatter.
+     * @param inDate 
+     * @return Formatted date
+     */
+    public synchronized String format(Date inDate)
+    {
+        return format(inDate.getTime());
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Format a date according to our stored formatter.
+     * @param inDate 
+     * @return Formatted date
+     */
+    public synchronized String format(long inDate)
+    {
+        long seconds = inDate / 1000;
+
+        // Is it not suitable to cache?
+        if (seconds<_lastSeconds ||
+            _lastSeconds>0 && seconds>_lastSeconds+__hitWindow)
+        {
+            // It's a cache miss
+            Date d = new Date(inDate);
+            return _tzFormat.format(d);
+            
+        }
+                                          
+        // Check if we are in the same second
+        // and don't care about millis
+        if (_lastSeconds==seconds )
+            return _lastResult;
+
+        Date d = new Date(inDate);
+        
+        // Check if we need a new format string
+        long minutes = seconds/60;
+        if (_lastMinutes != minutes)
+        {
+            _lastMinutes = minutes;
+            _secFormatString=_minFormat.format(d);
+
+            int i=_secFormatString.indexOf("ss");
+            int l=2;
+            _secFormatString0=_secFormatString.substring(0,i);
+            _secFormatString1=_secFormatString.substring(i+l);
+        }
+
+        // Always format if we get here
+        _lastSeconds = seconds;
+        StringBuilder sb=new StringBuilder(_secFormatString.length());
+        sb.append(_secFormatString0);
+        int s=(int)(seconds%60);
+        if (s<10)
+            sb.append('0');
+        sb.append(s);
+        sb.append(_secFormatString1);
+        _lastResult=sb.toString();
+
+                
+        return _lastResult;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Format to string buffer. 
+     * @param inDate Date the format
+     * @param buffer StringBuilder
+     */
+    public void format(long inDate, StringBuilder buffer)
+    {
+        buffer.append(format(inDate));
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Get the format.
+     */
+    public SimpleDateFormat getFormat()
+    {
+        return _minFormat;
+    }
+
+    /* ------------------------------------------------------------ */
+    public String getFormatString()
+    {
+        return _formatString;
+    }    
+
+    /* ------------------------------------------------------------ */
+    public String now()
+    {
+        long now=System.currentTimeMillis();
+        _lastMs=(int)(now%1000);
+        return format(now);
+    }
+
+    /* ------------------------------------------------------------ */
+    public int lastMs()
+    {
+        return _lastMs;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/HostMap.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,108 @@
+//
+//  ========================================================================
+//  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.util;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+/* ------------------------------------------------------------ */
+/**
+ */
+@SuppressWarnings("serial")
+public class HostMap<TYPE> extends HashMap<String, TYPE>
+{
+
+    /* --------------------------------------------------------------- */
+    /** Construct empty HostMap.
+     */
+    public HostMap()
+    {
+        super(11);
+    }
+   
+    /* --------------------------------------------------------------- */
+    /** Construct empty HostMap.
+     * 
+     * @param capacity initial capacity
+     */
+    public HostMap(int capacity)
+    {
+        super (capacity);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @see java.util.HashMap#put(java.lang.Object, java.lang.Object)
+     */
+    @Override
+    public TYPE put(String host, TYPE object)
+        throws IllegalArgumentException
+    {
+        return super.put(host, object);
+    }
+        
+    /* ------------------------------------------------------------ */
+    /**
+     * @see java.util.HashMap#get(java.lang.Object)
+     */
+    @Override
+    public TYPE get(Object key)
+    {
+        return super.get(key);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Retrieve a lazy list of map entries associated with specified
+     * hostname by taking into account the domain suffix matches.
+     * 
+     * @param host hostname
+     * @return lazy list of map entries
+     */
+    public Object getLazyMatches(String host)
+    {
+        if (host == null)
+            return LazyList.getList(super.entrySet());
+        
+        int idx = 0;
+        String domain = host.trim();
+        HashSet<String> domains = new HashSet<String>();
+        do {
+            domains.add(domain);
+            if ((idx = domain.indexOf('.')) > 0)
+            {
+                domain = domain.substring(idx+1);
+            }
+        } while (idx > 0);
+        
+        Object entries = null;
+        for(Map.Entry<String, TYPE> entry: super.entrySet())
+        {
+            if (domains.contains(entry.getKey()))
+            {
+                entries = LazyList.add(entries,entry);
+            }
+        }
+       
+        return entries;        
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/IO.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,556 @@
+//
+//  ========================================================================
+//  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.util;
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.io.Writer;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+
+/* ======================================================================== */
+/** IO Utilities.
+ * Provides stream handling utilities in
+ * singleton Threadpool implementation accessed by static members.
+ */
+public class IO 
+{
+    private static final Logger LOG = Log.getLogger(IO.class);
+    
+    /* ------------------------------------------------------------------- */
+    public final static String
+        CRLF      = "\015\012";
+
+    /* ------------------------------------------------------------------- */
+    public final static byte[]
+        CRLF_BYTES    = {(byte)'\015',(byte)'\012'};
+
+    /* ------------------------------------------------------------------- */
+    public static int bufferSize = 64*1024;
+    
+    /* ------------------------------------------------------------------- */
+    // TODO get rid of this singleton!
+    private static class Singleton {
+        static final QueuedThreadPool __pool=new QueuedThreadPool();
+        static
+        {
+            try{__pool.start();}
+            catch(Exception e){LOG.warn(e); System.exit(1);}
+        }
+    }
+
+    /* ------------------------------------------------------------------- */
+    static class Job implements Runnable
+    {
+        InputStream in;
+        OutputStream out;
+        Reader read;
+        Writer write;
+
+        Job(InputStream in,OutputStream out)
+        {
+            this.in=in;
+            this.out=out;
+            this.read=null;
+            this.write=null;
+        }
+        Job(Reader read,Writer write)
+        {
+            this.in=null;
+            this.out=null;
+            this.read=read;
+            this.write=write;
+        }
+        
+        /* ------------------------------------------------------------ */
+        /* 
+         * @see java.lang.Runnable#run()
+         */
+        public void run()
+        {
+            try {
+                if (in!=null)
+                    copy(in,out,-1);
+                else
+                    copy(read,write,-1);
+            }
+            catch(IOException e)
+            {
+                LOG.ignore(e);
+                try{
+                    if (out!=null)
+                        out.close();
+                    if (write!=null)
+                        write.close();
+                }
+                catch(IOException e2)
+                {
+                    LOG.ignore(e2);
+                }
+            }
+        }
+    }
+    
+    /* ------------------------------------------------------------------- */
+    /** Copy Stream in to Stream out until EOF or exception.
+     * in own thread
+     */
+    public static void copyThread(InputStream in, OutputStream out)
+    {
+        try{
+            Job job=new Job(in,out);
+            if (!Singleton.__pool.dispatch(job))
+                job.run();
+        }
+        catch(Exception e)
+        {
+            LOG.warn(e);
+        }
+    }
+    
+    /* ------------------------------------------------------------------- */
+    /** Copy Stream in to Stream out until EOF or exception.
+     */
+    public static void copy(InputStream in, OutputStream out)
+         throws IOException
+    {
+        copy(in,out,-1);
+    }
+    
+    /* ------------------------------------------------------------------- */
+    /** Copy Stream in to Stream out until EOF or exception
+     * in own thread
+     */
+    public static void copyThread(Reader in, Writer out)
+    {
+        try
+        {
+            Job job=new Job(in,out);
+            if (!Singleton.__pool.dispatch(job))
+                job.run();
+        }
+        catch(Exception e)
+        {
+            LOG.warn(e);
+        }
+    }
+    
+    /* ------------------------------------------------------------------- */
+    /** Copy Reader to Writer out until EOF or exception.
+     */
+    public static void copy(Reader in, Writer out)
+         throws IOException
+    {
+        copy(in,out,-1);
+    }
+    
+    /* ------------------------------------------------------------------- */
+    /** Copy Stream in to Stream for byteCount bytes or until EOF or exception.
+     */
+    public static void copy(InputStream in,
+                            OutputStream out,
+                            long byteCount)
+         throws IOException
+    {     
+        byte buffer[] = new byte[bufferSize];
+        int len=bufferSize;
+        
+        if (byteCount>=0)
+        {
+            while (byteCount>0)
+            {
+                int max = byteCount<bufferSize?(int)byteCount:bufferSize;
+                len=in.read(buffer,0,max);
+                
+                if (len==-1)
+                    break;
+                
+                byteCount -= len;
+                out.write(buffer,0,len);
+            }
+        }
+        else
+        {
+            while (true)
+            {
+                len=in.read(buffer,0,bufferSize);
+                if (len<0 )
+                    break;
+                out.write(buffer,0,len);
+            }
+        }
+    }  
+    
+    /* ------------------------------------------------------------------- */
+    /** Copy Reader to Writer for byteCount bytes or until EOF or exception.
+     */
+    public static void copy(Reader in,
+                            Writer out,
+                            long byteCount)
+         throws IOException
+    {  
+        char buffer[] = new char[bufferSize];
+        int len=bufferSize;
+        
+        if (byteCount>=0)
+        {
+            while (byteCount>0)
+            {
+                if (byteCount<bufferSize)
+                    len=in.read(buffer,0,(int)byteCount);
+                else
+                    len=in.read(buffer,0,bufferSize);                   
+                
+                if (len==-1)
+                    break;
+                
+                byteCount -= len;
+                out.write(buffer,0,len);
+            }
+        }
+        else if (out instanceof PrintWriter)
+        {
+            PrintWriter pout=(PrintWriter)out;
+            while (!pout.checkError())
+            {
+                len=in.read(buffer,0,bufferSize);
+                if (len==-1)
+                    break;
+                out.write(buffer,0,len);
+            }
+        }
+        else
+        {
+            while (true)
+            {
+                len=in.read(buffer,0,bufferSize);
+                if (len==-1)
+                    break;
+                out.write(buffer,0,len);
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Copy files or directories
+     * @param from
+     * @param to
+     * @throws IOException
+     */
+    public static void copy(File from,File to) throws IOException
+    {
+        if (from.isDirectory())
+            copyDir(from,to);
+        else
+            copyFile(from,to);
+    }
+
+    /* ------------------------------------------------------------ */
+    public static void copyDir(File from,File to) throws IOException
+    {
+        if (to.exists())
+        {
+            if (!to.isDirectory())
+                throw new IllegalArgumentException(to.toString());
+        }
+        else
+            to.mkdirs();
+        
+        File[] files = from.listFiles();
+        if (files!=null)
+        {
+            for (int i=0;i<files.length;i++)
+            {
+                String name = files[i].getName();
+                if (".".equals(name) || "..".equals(name))
+                    continue;
+                copy(files[i],new File(to,name));
+            }
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    public static void copyFile(File from,File to) throws IOException
+    {
+        FileInputStream in=new FileInputStream(from);
+        FileOutputStream out=new FileOutputStream(to);
+        copy(in,out);
+        in.close();
+        out.close();
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Read input stream to string.
+     */
+    public static String toString(InputStream in)
+        throws IOException
+    {
+        return toString(in,null);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Read input stream to string.
+     */
+    public static String toString(InputStream in,String encoding)
+        throws IOException
+    {
+        StringWriter writer=new StringWriter();
+        InputStreamReader reader = encoding==null?new InputStreamReader(in):new InputStreamReader(in,encoding);
+        
+        copy(reader,writer);
+        return writer.toString();
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Read input stream to string.
+     */
+    public static String toString(Reader in)
+        throws IOException
+    {
+        StringWriter writer=new StringWriter();
+        copy(in,writer);
+        return writer.toString();
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /** Delete File.
+     * This delete will recursively delete directories - BE CAREFULL
+     * @param file The file to be deleted.
+     */
+    public static boolean delete(File file)
+    {
+        if (!file.exists())
+            return false;
+        if (file.isDirectory())
+        {
+            File[] files = file.listFiles();
+            for (int i=0;files!=null && i<files.length;i++)
+                delete(files[i]);
+        }
+        return file.delete();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * closes any {@link Closeable}
+     *
+     * @param c the closeable to close
+     */
+    public static void close(Closeable c)
+    {
+        try
+        {
+            if (c != null)
+                c.close();
+        }
+        catch (IOException e)
+        {
+            LOG.ignore(e);
+        }
+    }
+    
+    /**
+     * closes an input stream, and logs exceptions
+     *
+     * @param is the input stream to close
+     */
+    public static void close(InputStream is)
+    {
+        try
+        {
+            if (is != null)
+                is.close();
+        }
+        catch (IOException e)
+        {
+            LOG.ignore(e);
+        }
+    }
+
+    /**
+     * closes a reader, and logs exceptions
+     * 
+     * @param reader the reader to close
+     */
+    public static void close(Reader reader)
+    {
+        try
+        {
+            if (reader != null)
+                reader.close();
+        } catch (IOException e)
+        {
+            LOG.ignore(e);
+        }
+    }
+
+    /**
+     * closes a writer, and logs exceptions
+     * 
+     * @param writer the writer to close
+     */
+    public static void close(Writer writer)
+    {
+        try
+        {
+            if (writer != null)
+                writer.close();
+        } catch (IOException e)
+        {
+            LOG.ignore(e);
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    public static byte[] readBytes(InputStream in)
+        throws IOException
+    {
+        ByteArrayOutputStream bout = new ByteArrayOutputStream();
+        copy(in,bout);
+        return bout.toByteArray();
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * closes an output stream, and logs exceptions
+     *
+     * @param os the output stream to close
+     */
+    public static void close(OutputStream os)
+    {
+        try
+        {
+            if (os != null)
+                os.close();
+        }
+        catch (IOException e)
+        {
+            LOG.ignore(e);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /** 
+     * @return An outputstream to nowhere
+     */
+    public static OutputStream getNullStream()
+    {
+        return __nullStream;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** 
+     * @return An outputstream to nowhere
+     */
+    public static InputStream getClosedStream()
+    {
+        return __closedStream;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    private static class NullOS extends OutputStream                                    
+    {
+        @Override
+        public void close(){}
+        @Override
+        public void flush(){}
+        @Override
+        public void write(byte[]b){}
+        @Override
+        public void write(byte[]b,int i,int l){}
+        @Override
+        public void write(int b){}
+    }
+    private static NullOS __nullStream = new NullOS();
+
+    
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    private static class ClosedIS extends InputStream                                    
+    {
+        @Override
+        public int read() throws IOException
+        {
+            return -1;
+        }
+    }
+    private static ClosedIS __closedStream = new ClosedIS();
+    
+    /* ------------------------------------------------------------ */
+    /** 
+     * @return An writer to nowhere
+     */
+    public static Writer getNullWriter()
+    {
+        return __nullWriter;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** 
+     * @return An writer to nowhere
+     */
+    public static PrintWriter getNullPrintWriter()
+    {
+        return __nullPrintWriter;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    private static class NullWrite extends Writer                                    
+    {
+        @Override
+        public void close(){}
+        @Override
+        public void flush(){}
+        @Override
+        public void write(char[]b){}
+        @Override
+        public void write(char[]b,int o,int l){}
+        @Override
+        public void write(int b){}
+        @Override
+        public void write(String s){}
+        @Override
+        public void write(String s,int o,int l){}
+    }
+    private static NullWrite __nullWriter = new NullWrite();
+    private static PrintWriter __nullPrintWriter = new PrintWriter(__nullWriter);
+}
+
+
+
+
+
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/IPAddressMap.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,364 @@
+//
+//  ========================================================================
+//  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.util;
+
+import java.util.BitSet;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+
+/* ------------------------------------------------------------ */
+/**
+ * Internet address map to object
+ * <p>
+ * Internet addresses may be specified as absolute address or as a combination of 
+ * four octet wildcard specifications (a.b.c.d) that are defined as follows.
+ * </p>
+ * <pre>
+ * nnn - an absolute value (0-255)
+ * mmm-nnn - an inclusive range of absolute values, 
+ *           with following shorthand notations:
+ *           nnn- => nnn-255
+ *           -nnn => 0-nnn
+ *           -    => 0-255
+ * a,b,... - a list of wildcard specifications
+ * </pre>
+ */
+@SuppressWarnings("serial")
+public class IPAddressMap<TYPE> extends HashMap<String, TYPE>
+{
+    private final HashMap<String,IPAddrPattern> _patterns = new HashMap<String,IPAddrPattern>();
+
+    /* --------------------------------------------------------------- */
+    /** Construct empty IPAddressMap.
+     */
+    public IPAddressMap()
+    {
+        super(11);
+    }
+   
+    /* --------------------------------------------------------------- */
+    /** Construct empty IPAddressMap.
+     * 
+     * @param capacity initial capacity
+     */
+    public IPAddressMap(int capacity)
+    {
+        super (capacity);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Insert a new internet address into map
+     * 
+     * @see java.util.HashMap#put(java.lang.Object, java.lang.Object)
+     */
+    @Override
+    public TYPE put(String addrSpec, TYPE object)
+        throws IllegalArgumentException
+    {
+        if (addrSpec == null || addrSpec.trim().length() == 0)
+            throw new IllegalArgumentException("Invalid IP address pattern: "+addrSpec);
+        
+        String spec = addrSpec.trim();
+        if (_patterns.get(spec) == null)
+            _patterns.put(spec,new IPAddrPattern(spec));
+        
+        return super.put(spec, object);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Retrieve the object mapped to the specified internet address literal
+     * 
+     * @see java.util.HashMap#get(java.lang.Object)
+     */
+    @Override
+    public TYPE get(Object key)
+    {
+        return super.get(key);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Retrieve the first object that is associated with the specified 
+     * internet address by taking into account the wildcard specifications.
+     * 
+     * @param addr internet address
+     * @return associated object
+     */
+    public TYPE match(String addr)
+    {
+        Map.Entry<String, TYPE> entry = getMatch(addr);
+        return entry==null ? null : entry.getValue();
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Retrieve the first map entry that is associated with the specified 
+     * internet address by taking into account the wildcard specifications.
+     * 
+     * @param addr internet address
+     * @return map entry associated
+     */
+    public Map.Entry<String, TYPE> getMatch(String addr)
+    {
+        if (addr != null)
+        {
+            for(Map.Entry<String, TYPE> entry: super.entrySet())
+            {
+                if (_patterns.get(entry.getKey()).match(addr))
+                {
+                    return entry;
+                }
+            }
+        }
+        return null;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Retrieve a lazy list of map entries associated with specified
+     * internet address by taking into account the wildcard specifications.
+     * 
+     * @param addr  internet address
+     * @return lazy list of map entries
+     */
+    public Object getLazyMatches(String addr)
+    {
+        if (addr == null)
+            return LazyList.getList(super.entrySet());
+        
+        Object entries = null;
+        for(Map.Entry<String, TYPE> entry: super.entrySet())
+        {
+            if (_patterns.get(entry.getKey()).match(addr))
+            {
+                entries = LazyList.add(entries,entry);
+            }
+        }
+        return entries;        
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * IPAddrPattern
+     * 
+     * Represents internet address wildcard. 
+     * Matches the wildcard to provided internet address.
+     */
+    private static class IPAddrPattern
+    {
+        private final OctetPattern[] _octets = new OctetPattern[4];
+        /* ------------------------------------------------------------ */
+        /**
+         * Create new IPAddrPattern
+         * 
+         * @param value internet address wildcard specification
+         * @throws IllegalArgumentException if wildcard specification is invalid
+         */
+        public IPAddrPattern(String value)
+            throws IllegalArgumentException
+        {
+            if (value == null || value.trim().length() == 0)
+                throw new IllegalArgumentException("Invalid IP address pattern: "+value);
+                
+            try
+            {
+                StringTokenizer parts = new StringTokenizer(value, ".");
+                
+                String part;
+                for (int idx=0; idx<4; idx++)
+                {
+                    part = parts.hasMoreTokens() ? parts.nextToken().trim() : "0-255";
+                    
+                    int len = part.length();
+                    if (len == 0 && parts.hasMoreTokens())
+                        throw new IllegalArgumentException("Invalid IP address pattern: "+value);
+                    
+                    _octets[idx] = new OctetPattern(len==0 ? "0-255" : part);
+                }
+            }
+            catch (IllegalArgumentException ex)
+            {
+                throw new IllegalArgumentException("Invalid IP address pattern: "+value, ex);
+            }
+        }
+        
+        /* ------------------------------------------------------------ */
+        /**
+         * Match the specified internet address against the wildcard
+         * 
+         * @param value internet address
+         * @return true if specified internet address matches wildcard specification
+         * 
+         * @throws IllegalArgumentException if specified internet address is invalid
+         */
+        public boolean match(String value)
+            throws IllegalArgumentException
+        {
+            if (value == null || value.trim().length() == 0)
+                throw new IllegalArgumentException("Invalid IP address: "+value);
+            
+            try
+            {
+                StringTokenizer parts = new StringTokenizer(value, ".");
+                
+                boolean result = true;
+                for (int idx=0; idx<4; idx++)
+                {
+                    if (!parts.hasMoreTokens())
+                        throw new IllegalArgumentException("Invalid IP address: "+value);
+                        
+                    if (!(result &= _octets[idx].match(parts.nextToken())))
+                        break;
+                }
+                return result;
+            }
+            catch (IllegalArgumentException ex)
+            {
+                throw new IllegalArgumentException("Invalid IP address: "+value, ex);
+            }
+        }
+    }
+        
+    /* ------------------------------------------------------------ */
+    /**
+     * OctetPattern
+     * 
+     * Represents a single octet wildcard.
+     * Matches the wildcard to the specified octet value.
+     */
+    private static class OctetPattern extends BitSet
+    {
+        private final BitSet _mask = new BitSet(256);
+        
+        /* ------------------------------------------------------------ */
+        /**
+         * Create new OctetPattern
+         * 
+         * @param octetSpec octet wildcard specification
+         * @throws IllegalArgumentException if wildcard specification is invalid
+         */
+        public OctetPattern(String octetSpec)
+            throws IllegalArgumentException
+        {
+            try
+            {
+                if (octetSpec != null)
+                {
+                    String spec = octetSpec.trim();
+                    if(spec.length() == 0)
+                    {
+                        _mask.set(0,255);
+                    }
+                    else
+                    {
+                        StringTokenizer parts = new StringTokenizer(spec,",");
+                        while (parts.hasMoreTokens())
+                        {
+                            String part = parts.nextToken().trim();
+                            if (part.length() > 0)
+                            {
+                                if (part.indexOf('-') < 0)
+                                {
+                                    Integer value = Integer.valueOf(part);
+                                    _mask.set(value);
+                                }
+                                else
+                                {
+                                    int low = 0, high = 255;
+                                    
+                                    String[] bounds = part.split("-",-2);
+                                    if (bounds.length != 2)
+                                    {
+                                        throw new IllegalArgumentException("Invalid octet spec: "+octetSpec);
+                                    }
+                                    
+                                    if (bounds[0].length() > 0)
+                                    {
+                                        low = Integer.parseInt(bounds[0]);
+                                    }
+                                    if (bounds[1].length() > 0)
+                                    {
+                                        high = Integer.parseInt(bounds[1]);
+                                    }
+                                    
+                                    if (low > high)
+                                    {
+                                        throw new IllegalArgumentException("Invalid octet spec: "+octetSpec);
+                                    }
+                                    
+                                    _mask.set(low, high+1);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            catch (NumberFormatException ex)
+            {
+                throw new IllegalArgumentException("Invalid octet spec: "+octetSpec, ex);
+            }
+        }
+        
+        /* ------------------------------------------------------------ */
+        /**
+         * Match specified octet value against the wildcard
+         * 
+         * @param value octet value
+         * @return true if specified octet value matches the wildcard
+         * @throws IllegalArgumentException if specified octet value is invalid
+         */
+        public boolean match(String value)
+            throws IllegalArgumentException
+        {
+            if (value == null || value.trim().length() == 0)
+                throw new IllegalArgumentException("Invalid octet: "+value);
+
+            try
+            {
+                int number = Integer.parseInt(value);
+                return match(number);
+            }
+            catch (NumberFormatException ex)
+            {
+                throw new IllegalArgumentException("Invalid octet: "+value);
+            }
+        }
+        
+        /* ------------------------------------------------------------ */
+        /**
+         * Match specified octet value against the wildcard
+         * 
+         * @param number octet value
+         * @return true if specified octet value matches the wildcard
+         * @throws IllegalArgumentException if specified octet value is invalid
+         */
+        public boolean match(int number)
+            throws IllegalArgumentException
+        {
+            if (number < 0 || number > 255)
+                throw new IllegalArgumentException("Invalid octet: "+number);
+            
+            return _mask.get(number);
+        }
+    }   
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/IntrospectionUtil.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,300 @@
+//
+//  ========================================================================
+//  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.util;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * IntrospectionUtil
+ *
+ *
+ */
+public class IntrospectionUtil
+{
+    
+    public static boolean isJavaBeanCompliantSetter (Method method)
+    {
+        if (method == null)
+            return false;
+        
+        if (method.getReturnType() != Void.TYPE)
+            return false;
+        
+        if (!method.getName().startsWith("set"))
+            return false;
+        
+        if (method.getParameterTypes().length != 1)
+            return false;
+        
+        return true;
+    }
+    
+    public static Method findMethod (Class<?> clazz, String methodName, Class<?>[] args, boolean checkInheritance, boolean strictArgs)
+    throws NoSuchMethodException
+    {
+        if (clazz == null)
+            throw new NoSuchMethodException("No class");
+        if (methodName==null || methodName.trim().equals(""))
+            throw new NoSuchMethodException("No method name");
+        
+        Method method = null;
+        Method[] methods = clazz.getDeclaredMethods();
+        for (int i=0;i<methods.length && method==null;i++)
+        {
+            if (methods[i].getName().equals(methodName) && checkParams(methods[i].getParameterTypes(), (args==null?new Class[] {}:args), strictArgs))
+            {
+                method = methods[i];
+            }
+            
+        }
+        if (method!=null)
+        {
+            return method;
+        }
+        else if (checkInheritance)
+                return findInheritedMethod(clazz.getPackage(), clazz.getSuperclass(), methodName, args, strictArgs);
+        else
+            throw new NoSuchMethodException("No such method "+methodName+" on class "+clazz.getName());
+
+    }
+    
+    
+    
+    
+
+    public static Field findField (Class<?> clazz, String targetName, Class<?> targetType, boolean checkInheritance, boolean strictType)
+    throws NoSuchFieldException
+    {
+        if (clazz == null)
+            throw new NoSuchFieldException("No class");
+        if (targetName==null)
+            throw new NoSuchFieldException("No field name");
+        
+        try
+        {
+            Field field = clazz.getDeclaredField(targetName);
+            if (strictType)
+            {
+                if (field.getType().equals(targetType))
+                    return field;
+            }
+            else
+            {
+                if (field.getType().isAssignableFrom(targetType))
+                    return field;
+            }
+            if (checkInheritance)
+            {
+                    return findInheritedField(clazz.getPackage(), clazz.getSuperclass(), targetName, targetType, strictType);
+            }
+            else
+                throw new NoSuchFieldException("No field with name "+targetName+" in class "+clazz.getName()+" of type "+targetType);
+        }
+        catch (NoSuchFieldException e)
+        {
+            return findInheritedField(clazz.getPackage(),clazz.getSuperclass(), targetName,targetType,strictType);
+        }
+    }
+    
+    
+    
+    
+    
+    public static boolean isInheritable (Package pack, Member member)
+    {
+        if (pack==null)
+            return false;
+        if (member==null)
+            return false;
+        
+        int modifiers = member.getModifiers();
+        if (Modifier.isPublic(modifiers))
+            return true;
+        if (Modifier.isProtected(modifiers))
+            return true;
+        if (!Modifier.isPrivate(modifiers) && pack.equals(member.getDeclaringClass().getPackage()))
+            return true;
+       
+        return false;
+    }
+    
+   
+    
+    
+    public static boolean checkParams (Class<?>[] formalParams, Class<?>[] actualParams, boolean strict)
+    {
+        if (formalParams==null)
+            return actualParams==null;
+        if (actualParams==null)
+            return false;
+
+        if (formalParams.length!=actualParams.length)
+            return false;
+
+        if (formalParams.length==0)
+            return true; 
+        
+        int j=0;
+        if (strict)
+        {
+            while (j<formalParams.length && formalParams[j].equals(actualParams[j]))
+                j++;
+        }
+        else
+        { 
+            while ((j<formalParams.length) && (formalParams[j].isAssignableFrom(actualParams[j])))
+            {
+                j++;
+            }
+        }
+
+        if (j!=formalParams.length)
+        {
+            return false;
+        }
+
+        return true;
+    }
+    
+    
+    public static boolean isSameSignature (Method methodA, Method methodB)
+    {
+        if (methodA==null)
+            return false;
+        if (methodB==null)
+            return false;
+        
+        List<Class<?>> parameterTypesA = Arrays.asList(methodA.getParameterTypes());
+        List<Class<?>> parameterTypesB = Arrays.asList(methodB.getParameterTypes());
+       
+        if (methodA.getName().equals(methodB.getName())
+            &&
+            parameterTypesA.containsAll(parameterTypesB))
+            return true;
+        
+        return false;
+    }
+    
+    public static boolean isTypeCompatible (Class<?> formalType, Class<?> actualType, boolean strict)
+    {
+        if (formalType==null)
+            return actualType==null;
+        if (actualType==null)
+            return false;
+        
+        if (strict)
+            return formalType.equals(actualType);
+        else
+            return formalType.isAssignableFrom(actualType);
+    }
+
+    
+    
+    
+    public static boolean containsSameMethodSignature (Method method, Class<?> c, boolean checkPackage)
+    {
+        if (checkPackage)
+        {
+            if (!c.getPackage().equals(method.getDeclaringClass().getPackage()))
+                return false;
+        }
+        
+        boolean samesig = false;
+        Method[] methods = c.getDeclaredMethods();
+        for (int i=0; i<methods.length && !samesig; i++)
+        {
+            if (IntrospectionUtil.isSameSignature(method, methods[i]))
+                samesig = true;
+        }
+        return samesig;
+    }
+    
+    
+    public static boolean containsSameFieldName(Field field, Class<?> c, boolean checkPackage)
+    {
+        if (checkPackage)
+        {
+            if (!c.getPackage().equals(field.getDeclaringClass().getPackage()))
+                return false;
+        }
+        
+        boolean sameName = false;
+        Field[] fields = c.getDeclaredFields();
+        for (int i=0;i<fields.length && !sameName; i++)
+        {
+            if (fields[i].getName().equals(field.getName()))
+                sameName = true;
+        }
+        return sameName;
+    }
+    
+    
+    
+    protected static Method findInheritedMethod (Package pack, Class<?> clazz, String methodName, Class<?>[] args, boolean strictArgs)
+    throws NoSuchMethodException
+    {
+        if (clazz==null)
+            throw new NoSuchMethodException("No class");
+        if (methodName==null)
+            throw new NoSuchMethodException("No method name");
+        
+        Method method = null;
+        Method[] methods = clazz.getDeclaredMethods();
+        for (int i=0;i<methods.length && method==null;i++)
+        {
+            if (methods[i].getName().equals(methodName) 
+                    && isInheritable(pack,methods[i])
+                    && checkParams(methods[i].getParameterTypes(), args, strictArgs))
+                method = methods[i];
+        }
+        if (method!=null)
+        {
+            return method;
+        }
+        else
+            return findInheritedMethod(clazz.getPackage(), clazz.getSuperclass(), methodName, args, strictArgs);
+    }
+    
+    protected static Field findInheritedField (Package pack, Class<?> clazz, String fieldName, Class<?> fieldType, boolean strictType)
+    throws NoSuchFieldException
+    {
+        if (clazz==null)
+            throw new NoSuchFieldException ("No class");
+        if (fieldName==null)
+            throw new NoSuchFieldException ("No field name");
+        try
+        {
+            Field field = clazz.getDeclaredField(fieldName);
+            if (isInheritable(pack, field) && isTypeCompatible(fieldType, field.getType(), strictType))
+                return field;
+            else
+                return findInheritedField(clazz.getPackage(), clazz.getSuperclass(),fieldName, fieldType, strictType);
+        }
+        catch (NoSuchFieldException e)
+        {
+            return findInheritedField(clazz.getPackage(), clazz.getSuperclass(),fieldName, fieldType, strictType); 
+        }
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/LazyList.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,483 @@
+//
+//  ========================================================================
+//  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.util;
+
+import java.io.Serializable;
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+/* ------------------------------------------------------------ */
+/** Lazy List creation.
+ * A List helper class that attempts to avoid unnecessary List
+ * creation.   If a method needs to create a List to return, but it is
+ * expected that this will either be empty or frequently contain a
+ * single item, then using LazyList will avoid additional object
+ * creations by using {@link Collections#EMPTY_LIST} or
+ * {@link Collections#singletonList(Object)} where possible.
+ * <p>
+ * LazyList works by passing an opaque representation of the list in
+ * and out of all the LazyList methods.  This opaque object is either
+ * null for an empty list, an Object for a list with a single entry
+ * or an {@link ArrayList} for a list of items.
+ *
+ * <p><h4>Usage</h4>
+ * <pre>
+ *   Object lazylist =null;
+ *   while(loopCondition)
+ *   {
+ *     Object item = getItem();
+ *     if (item.isToBeAdded())
+ *         lazylist = LazyList.add(lazylist,item);
+ *   }
+ *   return LazyList.getList(lazylist);
+ * </pre>
+ *
+ * An ArrayList of default size is used as the initial LazyList.
+ *
+ * @see java.util.List
+ */
+public class LazyList
+    implements Cloneable, Serializable
+{
+    private static final String[] __EMTPY_STRING_ARRAY = new String[0];
+    
+    /* ------------------------------------------------------------ */
+    private LazyList()
+    {}
+    
+    /* ------------------------------------------------------------ */
+    /** Add an item to a LazyList 
+     * @param list The list to add to or null if none yet created.
+     * @param item The item to add.
+     * @return The lazylist created or added to.
+     */
+    @SuppressWarnings("unchecked")
+    public static Object add(Object list, Object item)
+    {
+        if (list==null)
+        {
+            if (item instanceof List || item==null)
+            {
+                List<Object> l = new ArrayList<Object>();
+                l.add(item);
+                return l;
+            }
+
+            return item;
+        }
+
+        if (list instanceof List)
+        {
+            ((List<Object>)list).add(item);
+            return list;
+        }
+
+        List<Object> l=new ArrayList<Object>();
+        l.add(list);
+        l.add(item);
+        return l;    
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Add an item to a LazyList 
+     * @param list The list to add to or null if none yet created.
+     * @param index The index to add the item at.
+     * @param item The item to add.
+     * @return The lazylist created or added to.
+     */
+    @SuppressWarnings("unchecked")
+    public static Object add(Object list, int index, Object item)
+    {
+        if (list==null)
+        {
+            if (index>0 || item instanceof List || item==null)
+            {
+                List<Object> l = new ArrayList<Object>();
+                l.add(index,item);
+                return l;
+            }
+            return item;
+        }
+
+        if (list instanceof List)
+        {
+            ((List<Object>)list).add(index,item);
+            return list;
+        }
+
+        List<Object> l=new ArrayList<Object>();
+        l.add(list);
+        l.add(index,item);
+        return l;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Add the contents of a Collection to a LazyList
+     * @param list The list to add to or null if none yet created.
+     * @param collection The Collection whose contents should be added.
+     * @return The lazylist created or added to.
+     */
+    public static Object addCollection(Object list, Collection<?> collection)
+    {
+        Iterator<?> i=collection.iterator();
+        while(i.hasNext())
+            list=LazyList.add(list,i.next());
+        return list;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Add the contents of an array to a LazyList
+     * @param list The list to add to or null if none yet created.
+     * @param array The array whose contents should be added.
+     * @return The lazylist created or added to.
+     */
+    public static Object addArray(Object list, Object[] array)
+    {
+        for(int i=0;array!=null && i<array.length;i++)
+            list=LazyList.add(list,array[i]);
+        return list;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Ensure the capacity of the underlying list.
+     * 
+     */
+    public static Object ensureSize(Object list, int initialSize)
+    {
+        if (list==null)
+            return new ArrayList<Object>(initialSize);
+        if (list instanceof ArrayList)
+        {
+            ArrayList<?> ol=(ArrayList<?>)list;
+            if (ol.size()>initialSize)
+                return ol;
+            ArrayList<Object> nl = new ArrayList<Object>(initialSize);
+            nl.addAll(ol);
+            return nl;
+        }
+        List<Object> l= new ArrayList<Object>(initialSize);
+        l.add(list);
+        return l;    
+    }
+
+    /* ------------------------------------------------------------ */
+    public static Object remove(Object list, Object o)
+    {
+        if (list==null)
+            return null;
+
+        if (list instanceof List)
+        {
+            List<?> l = (List<?>)list;
+            l.remove(o);
+            if (l.size()==0)
+                return null;
+            return list;
+        }
+
+        if (list.equals(o))
+            return null;
+        return list;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public static Object remove(Object list, int i)
+    {
+        if (list==null)
+            return null;
+
+        if (list instanceof List)
+        {
+            List<?> l = (List<?>)list;
+            l.remove(i);
+            if (l.size()==0)
+                return null;
+            return list;
+        }
+
+        if (i==0)
+            return null;
+        return list;
+    }
+    
+    
+    
+    /* ------------------------------------------------------------ */
+    /** Get the real List from a LazyList.
+     * 
+     * @param list A LazyList returned from LazyList.add(Object)
+     * @return The List of added items, which may be an EMPTY_LIST
+     * or a SingletonList.
+     */
+    public static<E> List<E> getList(Object list)
+    {
+        return getList(list,false);
+    }
+    
+
+    /* ------------------------------------------------------------ */
+    /** Get the real List from a LazyList.
+     * 
+     * @param list A LazyList returned from LazyList.add(Object) or null
+     * @param nullForEmpty If true, null is returned instead of an
+     * empty list.
+     * @return The List of added items, which may be null, an EMPTY_LIST
+     * or a SingletonList.
+     */
+    @SuppressWarnings("unchecked")
+    public static<E> List<E> getList(Object list, boolean nullForEmpty)
+    {
+        if (list==null)
+        {
+            if (nullForEmpty)
+                return null;
+            return Collections.emptyList();
+        }
+        if (list instanceof List)
+            return (List<E>)list;
+        
+        return (List<E>)Collections.singletonList(list);
+    }
+
+    
+    /* ------------------------------------------------------------ */
+    public static String[] toStringArray(Object list)
+    {
+        if (list==null)
+            return __EMTPY_STRING_ARRAY;
+        
+        if (list instanceof List)
+        {
+            List<?> l = (List<?>)list;
+            String[] a = new String[l.size()];
+            for (int i=l.size();i-->0;)
+            {
+                Object o=l.get(i);
+                if (o!=null)
+                    a[i]=o.toString();
+            }
+            return a;
+        }
+        
+        return new String[] {list.toString()};
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Convert a lazylist to an array
+     * @param list The list to convert
+     * @param clazz The class of the array, which may be a primitive type
+     * @return array of the lazylist entries passed in
+     */
+    public static Object toArray(Object list,Class<?> clazz)
+    {
+        if (list==null)
+            return Array.newInstance(clazz,0);
+        
+        if (list instanceof List)
+        {
+            List<?> l = (List<?>)list;
+            if (clazz.isPrimitive())
+            {
+                Object a = Array.newInstance(clazz,l.size());
+                for (int i=0;i<l.size();i++)
+                    Array.set(a,i,l.get(i));
+                return a;
+            }
+            return l.toArray((Object[])Array.newInstance(clazz,l.size()));
+            
+        }
+        
+        Object a = Array.newInstance(clazz,1);
+        Array.set(a,0,list);
+        return a;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** The size of a lazy List 
+     * @param list  A LazyList returned from LazyList.add(Object) or null
+     * @return the size of the list.
+     */
+    public static int size(Object list)
+    {
+        if (list==null)
+            return 0;
+        if (list instanceof List)
+            return ((List<?>)list).size();
+        return 1;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Get item from the list 
+     * @param list  A LazyList returned from LazyList.add(Object) or null
+     * @param i int index
+     * @return the item from the list.
+     */
+    @SuppressWarnings("unchecked")
+    public static <E> E get(Object list, int i)
+    {
+        if (list==null)
+            throw new IndexOutOfBoundsException();
+       
+        if (list instanceof List)
+            return (E)((List<?>)list).get(i);
+
+        if (i==0)
+            return (E)list;
+        
+        throw new IndexOutOfBoundsException();
+    }
+    
+    /* ------------------------------------------------------------ */
+    public static boolean contains(Object list,Object item)
+    {
+        if (list==null)
+            return false;
+        
+        if (list instanceof List)
+            return ((List<?>)list).contains(item);
+
+        return list.equals(item);
+    }
+    
+
+    /* ------------------------------------------------------------ */
+    public static Object clone(Object list)
+    {
+        if (list==null)
+            return null;
+        if (list instanceof List)
+            return new ArrayList<Object>((List<?>)list);
+        return list;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public static String toString(Object list)
+    {
+        if (list==null)
+            return "[]";
+        if (list instanceof List)
+            return list.toString();
+        return "["+list+"]";
+    }
+
+    /* ------------------------------------------------------------ */
+    @SuppressWarnings("unchecked")
+    public static<E> Iterator<E> iterator(Object list)
+    {
+        if (list==null)
+        {
+            List<E> empty=Collections.emptyList();
+            return empty.iterator();
+        }
+        if (list instanceof List)
+        {
+            return ((List<E>)list).iterator();
+        }
+        List<E> l=getList(list);
+        return l.iterator();
+    }
+    
+    /* ------------------------------------------------------------ */
+    @SuppressWarnings("unchecked")
+    public static<E> ListIterator<E> listIterator(Object list)
+    {
+        if (list==null)
+        {
+            List<E> empty=Collections.emptyList();
+            return empty.listIterator();
+        }
+        if (list instanceof List)
+            return ((List<E>)list).listIterator();
+
+        List<E> l=getList(list);
+        return l.listIterator();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param array Any array of object
+     * @return A new <i>modifiable</i> list initialised with the elements from <code>array</code>.
+     */
+    public static<E> List<E> array2List(E[] array)
+    {	
+        if (array==null || array.length==0)
+            return new ArrayList<E>();
+        return new ArrayList<E>(Arrays.asList(array));
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Add element to an array
+     * @param array The array to add to (or null)
+     * @param item The item to add
+     * @param type The type of the array (in case of null array)
+     * @return new array with contents of array plus item
+     */
+    public static<T> T[] addToArray(T[] array, T item, Class<?> type)
+    {
+        if (array==null)
+        {
+            if (type==null && item!=null)
+                type= item.getClass();
+            @SuppressWarnings("unchecked")
+            T[] na = (T[])Array.newInstance(type, 1);
+            na[0]=item;
+            return na;
+        }
+        else
+        {
+            // TODO: Replace with Arrays.copyOf(T[] original, int newLength) from Java 1.6+
+            Class<?> c = array.getClass().getComponentType();
+            @SuppressWarnings("unchecked")
+            T[] na = (T[])Array.newInstance(c, Array.getLength(array)+1);
+            System.arraycopy(array, 0, na, 0, array.length);
+            na[array.length]=item;
+            return na;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public static<T> T[] removeFromArray(T[] array, Object item)
+    {
+        if (item==null || array==null)
+            return array;
+        for (int i=array.length;i-->0;)
+        {
+            if (item.equals(array[i]))
+            {
+                Class<?> c = array==null?item.getClass():array.getClass().getComponentType();
+                @SuppressWarnings("unchecked")
+                T[] na = (T[])Array.newInstance(c, Array.getLength(array)-1);
+                if (i>0)
+                    System.arraycopy(array, 0, na, 0, i);
+                if (i+1<array.length)
+                    System.arraycopy(array, i+1, na, i, array.length-(i+1));
+                return na;
+            }
+        }
+        return array;
+    }
+    
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/Loader.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,193 @@
+//
+//  ========================================================================
+//  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.util;
+
+import java.io.File;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import org.eclipse.jetty.util.resource.Resource;
+
+/* ------------------------------------------------------------ */
+/** ClassLoader Helper.
+ * This helper class allows classes to be loaded either from the
+ * Thread's ContextClassLoader, the classloader of the derived class
+ * or the system ClassLoader.
+ *
+ * <B>Usage:</B><PRE>
+ * public class MyClass {
+ *     void myMethod() {
+ *          ...
+ *          Class c=Loader.loadClass(this.getClass(),classname);
+ *          ...
+ *     }
+ * </PRE>          
+ * 
+ */
+public class Loader
+{
+    /* ------------------------------------------------------------ */
+    public static URL getResource(Class<?> loadClass,String name, boolean checkParents)
+    {
+        URL url =null;
+        ClassLoader loader=Thread.currentThread().getContextClassLoader();
+        while (url==null && loader!=null )
+        {
+            url=loader.getResource(name); 
+            loader=(url==null&&checkParents)?loader.getParent():null;
+        }      
+        
+        loader=loadClass==null?null:loadClass.getClassLoader();
+        while (url==null && loader!=null )
+        {
+            url=loader.getResource(name); 
+            loader=(url==null&&checkParents)?loader.getParent():null;
+        }       
+
+        if (url==null)
+        {
+            url=ClassLoader.getSystemResource(name);
+        }   
+
+        return url;
+    }
+
+    /* ------------------------------------------------------------ */
+    @SuppressWarnings("rawtypes")
+    public static Class loadClass(Class loadClass,String name)
+        throws ClassNotFoundException
+    {
+        return loadClass(loadClass,name,false);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Load a class.
+     * 
+     * @param loadClass
+     * @param name
+     * @param checkParents If true, try loading directly from parent classloaders.
+     * @return Class
+     * @throws ClassNotFoundException
+     */
+    @SuppressWarnings("rawtypes")
+    public static Class loadClass(Class loadClass,String name,boolean checkParents)
+        throws ClassNotFoundException
+    {
+        ClassNotFoundException ex=null;
+        Class<?> c =null;
+        ClassLoader loader=Thread.currentThread().getContextClassLoader();
+        while (c==null && loader!=null )
+        {
+            try { c=loader.loadClass(name); }
+            catch (ClassNotFoundException e) {if(ex==null)ex=e;}
+            loader=(c==null&&checkParents)?loader.getParent():null;
+        }      
+        
+        loader=loadClass==null?null:loadClass.getClassLoader();
+        while (c==null && loader!=null )
+        {
+            try { c=loader.loadClass(name); }
+            catch (ClassNotFoundException e) {if(ex==null)ex=e;}
+            loader=(c==null&&checkParents)?loader.getParent():null;
+        }       
+
+        if (c==null)
+        {
+            try { c=Class.forName(name); }
+            catch (ClassNotFoundException e) {if(ex==null)ex=e;}
+        }   
+
+        if (c!=null)
+            return c;
+        throw ex;
+    }
+    
+    
+    
+    /* ------------------------------------------------------------ */
+    public static ResourceBundle getResourceBundle(Class<?> loadClass,String name,boolean checkParents, Locale locale)
+        throws MissingResourceException
+    {
+        MissingResourceException ex=null;
+        ResourceBundle bundle =null;
+        ClassLoader loader=Thread.currentThread().getContextClassLoader();
+        while (bundle==null && loader!=null )
+        {
+            try { bundle=ResourceBundle.getBundle(name, locale, loader); }
+            catch (MissingResourceException e) {if(ex==null)ex=e;}
+            loader=(bundle==null&&checkParents)?loader.getParent():null;
+        }      
+        
+        loader=loadClass==null?null:loadClass.getClassLoader();
+        while (bundle==null && loader!=null )
+        {
+            try { bundle=ResourceBundle.getBundle(name, locale, loader); }
+            catch (MissingResourceException e) {if(ex==null)ex=e;}
+            loader=(bundle==null&&checkParents)?loader.getParent():null;
+        }       
+
+        if (bundle==null)
+        {
+            try { bundle=ResourceBundle.getBundle(name, locale); }
+            catch (MissingResourceException e) {if(ex==null)ex=e;}
+        }   
+
+        if (bundle!=null)
+            return bundle;
+        throw ex;
+    }
+    
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Generate the classpath (as a string) of all classloaders
+     * above the given classloader.
+     * 
+     * This is primarily used for jasper.
+     * @return the system class path
+     */
+    public static String getClassPath(ClassLoader loader) throws Exception
+    {
+        StringBuilder classpath=new StringBuilder();
+        while (loader != null && (loader instanceof URLClassLoader))
+        {
+            URL[] urls = ((URLClassLoader)loader).getURLs();
+            if (urls != null)
+            {     
+                for (int i=0;i<urls.length;i++)
+                {
+                    Resource resource = Resource.newResource(urls[i]);
+                    File file=resource.getFile();
+                    if (file!=null && file.exists())
+                    {
+                        if (classpath.length()>0)
+                            classpath.append(File.pathSeparatorChar);
+                        classpath.append(file.getAbsolutePath());
+                    }
+                }
+            }
+            loader = loader.getParent();
+        }
+        return classpath.toString();
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/MultiException.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,185 @@
+//
+//  ========================================================================
+//  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.util;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.util.List;
+
+
+/* ------------------------------------------------------------ */
+/** Wraps multiple exceptions.
+ *
+ * Allows multiple exceptions to be thrown as a single exception.
+ *
+ * 
+ */
+@SuppressWarnings("serial")
+public class MultiException extends Exception
+{
+    private Object nested;
+
+    /* ------------------------------------------------------------ */
+    public MultiException()
+    {
+        super("Multiple exceptions");
+    }
+
+    /* ------------------------------------------------------------ */
+    public void add(Throwable e)
+    {
+        if (e instanceof MultiException)
+        {
+            MultiException me = (MultiException)e;
+            for (int i=0;i<LazyList.size(me.nested);i++)
+                nested=LazyList.add(nested,LazyList.get(me.nested,i));
+        }
+        else
+            nested=LazyList.add(nested,e);
+    }
+
+    /* ------------------------------------------------------------ */
+    public int size()
+    {
+        return LazyList.size(nested);
+    }
+    
+    /* ------------------------------------------------------------ */
+    public List<Throwable> getThrowables()
+    {
+        return LazyList.getList(nested);
+    }
+    
+    /* ------------------------------------------------------------ */
+    public Throwable getThrowable(int i)
+    {
+        return (Throwable) LazyList.get(nested,i);
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Throw a multiexception.
+     * If this multi exception is empty then no action is taken. If it
+     * contains a single exception that is thrown, otherwise the this
+     * multi exception is thrown. 
+     * @exception Exception 
+     */
+    public void ifExceptionThrow()
+        throws Exception
+    {
+        switch (LazyList.size(nested))
+        {
+          case 0:
+              break;
+          case 1:
+              Throwable th=(Throwable)LazyList.get(nested,0);
+              if (th instanceof Error)
+                  throw (Error)th;
+              if (th instanceof Exception)
+                  throw (Exception)th;
+          default:
+              throw this;
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Throw a Runtime exception.
+     * If this multi exception is empty then no action is taken. If it
+     * contains a single error or runtime exception that is thrown, otherwise the this
+     * multi exception is thrown, wrapped in a runtime exception. 
+     * @exception Error If this exception contains exactly 1 {@link Error} 
+     * @exception RuntimeException If this exception contains 1 {@link Throwable} but it is not an error,
+     *                             or it contains more than 1 {@link Throwable} of any type.
+     */
+    public void ifExceptionThrowRuntime()
+        throws Error
+    {
+        switch (LazyList.size(nested))
+        {
+          case 0:
+              break;
+          case 1:
+              Throwable th=(Throwable)LazyList.get(nested,0);
+              if (th instanceof Error)
+                  throw (Error)th;
+              else if (th instanceof RuntimeException)
+                  throw (RuntimeException)th;
+              else
+                  throw new RuntimeException(th);
+          default:
+              throw new RuntimeException(this);
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Throw a multiexception.
+     * If this multi exception is empty then no action is taken. If it
+     * contains a any exceptions then this
+     * multi exception is thrown. 
+     */
+    public void ifExceptionThrowMulti()
+        throws MultiException
+    {
+        if (LazyList.size(nested)>0)
+            throw this;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public String toString()
+    {
+        if (LazyList.size(nested)>0)
+            return MultiException.class.getSimpleName()+
+                LazyList.getList(nested);
+        return MultiException.class.getSimpleName()+"[]";
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void printStackTrace()
+    {
+        super.printStackTrace();
+        for (int i=0;i<LazyList.size(nested);i++)
+            ((Throwable)LazyList.get(nested,i)).printStackTrace();
+    }
+   
+
+    /* ------------------------------------------------------------------------------- */
+    /**
+     * @see java.lang.Throwable#printStackTrace(java.io.PrintStream)
+     */
+    @Override
+    public void printStackTrace(PrintStream out)
+    {
+        super.printStackTrace(out);
+        for (int i=0;i<LazyList.size(nested);i++)
+            ((Throwable)LazyList.get(nested,i)).printStackTrace(out);
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    /**
+     * @see java.lang.Throwable#printStackTrace(java.io.PrintWriter)
+     */
+    @Override
+    public void printStackTrace(PrintWriter out)
+    {
+        super.printStackTrace(out);
+        for (int i=0;i<LazyList.size(nested);i++)
+            ((Throwable)LazyList.get(nested,i)).printStackTrace(out);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/MultiMap.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,415 @@
+//
+//  ========================================================================
+//  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.util;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/* ------------------------------------------------------------ */
+/** A multi valued Map.
+ * This Map specializes HashMap and provides methods
+ * that operate on multi valued items. 
+ * <P>
+ * Implemented as a map of LazyList values
+ * @param <K> The key type of the map.
+ *
+ * @see LazyList
+ * 
+ */
+public class MultiMap<K> implements ConcurrentMap<K,Object>, Serializable
+{
+    private static final long serialVersionUID = -6878723138353851005L;
+    Map<K,Object> _map;
+    ConcurrentMap<K, Object> _cmap;
+
+    public MultiMap()
+    {
+        _map=new HashMap<K, Object>();
+    }
+    
+    public MultiMap(Map<K,Object> map)
+    {
+        if (map instanceof ConcurrentMap)
+            _map=_cmap=new ConcurrentHashMap<K, Object>(map);
+        else
+            _map=new HashMap<K, Object>(map);
+    }
+    
+    public MultiMap(MultiMap<K> map)
+    {
+        if (map._cmap!=null)
+            _map=_cmap=new ConcurrentHashMap<K, Object>(map._cmap);
+        else
+            _map=new HashMap<K,Object>(map._map);
+    }
+    
+    public MultiMap(int capacity)
+    {
+        _map=new HashMap<K, Object>(capacity);
+    }
+    
+    public MultiMap(boolean concurrent)
+    {
+        if (concurrent)
+            _map=_cmap=new ConcurrentHashMap<K, Object>();
+        else
+            _map=new HashMap<K, Object>();
+    }
+    
+
+    /* ------------------------------------------------------------ */
+    /** Get multiple values.
+     * Single valued entries are converted to singleton lists.
+     * @param name The entry key. 
+     * @return Unmodifieable List of values.
+     */
+    public List getValues(Object name)
+    {
+        return LazyList.getList(_map.get(name),true);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Get a value from a multiple value.
+     * If the value is not a multivalue, then index 0 retrieves the
+     * value or null.
+     * @param name The entry key.
+     * @param i Index of element to get.
+     * @return Unmodifieable List of values.
+     */
+    public Object getValue(Object name,int i)
+    {
+        Object l=_map.get(name);
+        if (i==0 && LazyList.size(l)==0)
+            return null;
+        return LazyList.get(l,i);
+    }
+    
+    
+    /* ------------------------------------------------------------ */
+    /** Get value as String.
+     * Single valued items are converted to a String with the toString()
+     * Object method. Multi valued entries are converted to a comma separated
+     * List.  No quoting of commas within values is performed.
+     * @param name The entry key. 
+     * @return String value.
+     */
+    public String getString(Object name)
+    {
+        Object l=_map.get(name);
+        switch(LazyList.size(l))
+        {
+          case 0:
+              return null;
+          case 1:
+              Object o=LazyList.get(l,0);
+              return o==null?null:o.toString();
+          default:
+          {
+              StringBuilder values=new StringBuilder(128);
+              for (int i=0; i<LazyList.size(l); i++)              
+              {
+                  Object e=LazyList.get(l,i);
+                  if (e!=null)
+                  {
+                      if (values.length()>0)
+                          values.append(',');
+                      values.append(e.toString());
+                  }
+              }   
+              return values.toString();
+          }
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    public Object get(Object name) 
+    {
+        Object l=_map.get(name);
+        switch(LazyList.size(l))
+        {
+          case 0:
+              return null;
+          case 1:
+              Object o=LazyList.get(l,0);
+              return o;
+          default:
+              return LazyList.getList(l,true);
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Put and entry into the map.
+     * @param name The entry key. 
+     * @param value The entry value.
+     * @return The previous value or null.
+     */
+    public Object put(K name, Object value) 
+    {
+        return _map.put(name,LazyList.add(null,value));
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Put multi valued entry.
+     * @param name The entry key. 
+     * @param values The List of multiple values.
+     * @return The previous value or null.
+     */
+    public Object putValues(K name, List<? extends Object> values) 
+    {
+        return _map.put(name,values);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Put multi valued entry.
+     * @param name The entry key. 
+     * @param values The String array of multiple values.
+     * @return The previous value or null.
+     */
+    public Object putValues(K name, String... values) 
+    {
+        Object list=null;
+        for (int i=0;i<values.length;i++)
+            list=LazyList.add(list,values[i]);
+        return _map.put(name,list);
+    }
+    
+    
+    /* ------------------------------------------------------------ */
+    /** Add value to multi valued entry.
+     * If the entry is single valued, it is converted to the first
+     * value of a multi valued entry.
+     * @param name The entry key. 
+     * @param value The entry value.
+     */
+    public void add(K name, Object value) 
+    {
+        Object lo = _map.get(name);
+        Object ln = LazyList.add(lo,value);
+        if (lo!=ln)
+            _map.put(name,ln);
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Add values to multi valued entry.
+     * If the entry is single valued, it is converted to the first
+     * value of a multi valued entry.
+     * @param name The entry key. 
+     * @param values The List of multiple values.
+     */
+    public void addValues(K name, List<? extends Object> values) 
+    {
+        Object lo = _map.get(name);
+        Object ln = LazyList.addCollection(lo,values);
+        if (lo!=ln)
+            _map.put(name,ln);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Add values to multi valued entry.
+     * If the entry is single valued, it is converted to the first
+     * value of a multi valued entry.
+     * @param name The entry key. 
+     * @param values The String array of multiple values.
+     */
+    public void addValues(K name, String[] values) 
+    {
+        Object lo = _map.get(name);
+        Object ln = LazyList.addCollection(lo,Arrays.asList(values));
+        if (lo!=ln)
+            _map.put(name,ln);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Remove value.
+     * @param name The entry key. 
+     * @param value The entry value. 
+     * @return true if it was removed.
+     */
+    public boolean removeValue(K name,Object value)
+    {
+        Object lo = _map.get(name);
+        Object ln=lo;
+        int s=LazyList.size(lo);
+        if (s>0)
+        {
+            ln=LazyList.remove(lo,value);
+            if (ln==null)
+                _map.remove(name);
+            else
+                _map.put(name, ln);
+        }
+        return LazyList.size(ln)!=s;
+    }
+    
+    
+    /* ------------------------------------------------------------ */
+    /** Put all contents of map.
+     * @param m Map
+     */
+    public void putAll(Map<? extends K, ? extends Object> m)
+    {
+        boolean multi = (m instanceof MultiMap);
+
+        if (multi)
+        {
+            for (Map.Entry<? extends K, ? extends Object> entry : m.entrySet())
+            {
+                _map.put(entry.getKey(),LazyList.clone(entry.getValue()));
+            }
+        }
+        else
+        {
+            _map.putAll(m);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /** 
+     * @return Map of String arrays
+     */
+    public Map<K,String[]> toStringArrayMap()
+    {
+        HashMap<K,String[]> map = new HashMap<K,String[]>(_map.size()*3/2)
+        {
+            public String toString()
+            {
+                StringBuilder b=new StringBuilder();
+                b.append('{');
+                for (K k:keySet())
+                {
+                    if(b.length()>1)
+                        b.append(',');
+                    b.append(k);
+                    b.append('=');
+                    b.append(Arrays.asList(get(k)));
+                }
+
+                b.append('}');
+                return b.toString();
+            }
+        };
+        
+        for(Map.Entry<K,Object> entry: _map.entrySet())
+        {
+            String[] a = LazyList.toStringArray(entry.getValue());
+            map.put(entry.getKey(),a);
+        }
+        return map;
+    }
+
+    @Override
+    public String toString()
+    {
+        return _cmap==null?_map.toString():_cmap.toString();
+    }
+    
+    public void clear()
+    {
+        _map.clear();
+    }
+
+    public boolean containsKey(Object key)
+    {
+        return _map.containsKey(key);
+    }
+
+    public boolean containsValue(Object value)
+    {
+        return _map.containsValue(value);
+    }
+
+    public Set<Entry<K, Object>> entrySet()
+    {
+        return _map.entrySet();
+    }
+
+    @Override
+    public boolean equals(Object o)
+    {
+        return _map.equals(o);
+    }
+
+    @Override
+    public int hashCode()
+    {
+        return _map.hashCode();
+    }
+
+    public boolean isEmpty()
+    {
+        return _map.isEmpty();
+    }
+
+    public Set<K> keySet()
+    {
+        return _map.keySet();
+    }
+
+    public Object remove(Object key)
+    {
+        return _map.remove(key);
+    }
+
+    public int size()
+    {
+        return _map.size();
+    }
+
+    public Collection<Object> values()
+    {
+        return _map.values();
+    }
+
+    
+    
+    public Object putIfAbsent(K key, Object value)
+    {
+        if (_cmap==null)
+            throw new UnsupportedOperationException();
+        return _cmap.putIfAbsent(key,value);
+    }
+
+    public boolean remove(Object key, Object value)
+    {
+        if (_cmap==null)
+            throw new UnsupportedOperationException();
+        return _cmap.remove(key,value);
+    }
+
+    public boolean replace(K key, Object oldValue, Object newValue)
+    {
+        if (_cmap==null)
+            throw new UnsupportedOperationException();
+        return _cmap.replace(key,oldValue,newValue);
+    }
+
+    public Object replace(K key, Object value)
+    {
+        if (_cmap==null)
+            throw new UnsupportedOperationException();
+        return _cmap.replace(key,value);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/MultiPartInputStream.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,851 @@
+//
+//  ========================================================================
+//  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.util;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import javax.servlet.MultipartConfigElement;
+import javax.servlet.ServletException;
+import javax.servlet.http.Part;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+
+
+/**
+ * MultiPartInputStream
+ *
+ * Handle a MultiPart Mime input stream, breaking it up on the boundary into files and strings.
+ */
+public class MultiPartInputStream
+{
+    private static final Logger LOG = Log.getLogger(MultiPartInputStream.class);
+
+    public static final MultipartConfigElement  __DEFAULT_MULTIPART_CONFIG = new MultipartConfigElement(System.getProperty("java.io.tmpdir"));
+    protected InputStream _in;
+    protected MultipartConfigElement _config;
+    protected String _contentType;
+    protected MultiMap<String> _parts;
+    protected File _tmpDir;
+    protected File _contextTmpDir;
+    protected boolean _deleteOnExit;
+    
+    
+    
+    public class MultiPart implements Part
+    {
+        protected String _name;
+        protected String _filename;
+        protected File _file;
+        protected OutputStream _out;
+        protected ByteArrayOutputStream2 _bout;
+        protected String _contentType;
+        protected MultiMap<String> _headers;
+        protected long _size = 0;
+        protected boolean _temporary = true;
+
+        public MultiPart (String name, String filename) 
+        throws IOException
+        {
+            _name = name;
+            _filename = filename;
+        }
+
+        protected void setContentType (String contentType)
+        {
+            _contentType = contentType;
+        }
+        
+        
+        protected void open() 
+        throws IOException
+        {
+            //We will either be writing to a file, if it has a filename on the content-disposition
+            //and otherwise a byte-array-input-stream, OR if we exceed the getFileSizeThreshold, we
+            //will need to change to write to a file.           
+            if (_filename != null && _filename.trim().length() > 0)
+            {
+                createFile();            
+            }
+            else
+            {
+                //Write to a buffer in memory until we discover we've exceed the 
+                //MultipartConfig fileSizeThreshold
+                _out = _bout= new ByteArrayOutputStream2();
+            }
+        }
+        
+        protected void close() 
+        throws IOException
+        {
+            _out.close();
+        }
+        
+      
+        protected void write (int b)
+        throws IOException
+        {      
+            if (MultiPartInputStream.this._config.getMaxFileSize() > 0 && _size + 1 > MultiPartInputStream.this._config.getMaxFileSize())
+                throw new IllegalStateException ("Multipart Mime part "+_name+" exceeds max filesize");
+            
+            if (MultiPartInputStream.this._config.getFileSizeThreshold() > 0 && _size + 1 > MultiPartInputStream.this._config.getFileSizeThreshold() && _file==null)
+                createFile();
+            _out.write(b);   
+            _size ++;
+        }
+        
+        protected void write (byte[] bytes, int offset, int length) 
+        throws IOException
+        { 
+            if (MultiPartInputStream.this._config.getMaxFileSize() > 0 && _size + length > MultiPartInputStream.this._config.getMaxFileSize())
+                throw new IllegalStateException ("Multipart Mime part "+_name+" exceeds max filesize");
+            
+            if (MultiPartInputStream.this._config.getFileSizeThreshold() > 0 && _size + length > MultiPartInputStream.this._config.getFileSizeThreshold() && _file==null)
+                createFile();
+            
+            _out.write(bytes, offset, length);
+            _size += length;
+        }
+        
+        protected void createFile ()
+        throws IOException
+        {
+            _file = File.createTempFile("MultiPart", "", MultiPartInputStream.this._tmpDir);
+            if (_deleteOnExit)
+                _file.deleteOnExit();
+            FileOutputStream fos = new FileOutputStream(_file);
+            BufferedOutputStream bos = new BufferedOutputStream(fos);
+            
+            if (_size > 0 && _out != null)
+            {
+                //already written some bytes, so need to copy them into the file
+                _out.flush();
+                _bout.writeTo(bos);
+                _out.close();
+                _bout = null;
+            }
+            _out = bos;
+        }
+        
+
+        
+        protected void setHeaders(MultiMap<String> headers)
+        {
+            _headers = headers;
+        }
+        
+        /** 
+         * @see javax.servlet.http.Part#getContentType()
+         */
+        public String getContentType()
+        {
+            return _contentType;
+        }
+
+        /** 
+         * @see javax.servlet.http.Part#getHeader(java.lang.String)
+         */
+        public String getHeader(String name)
+        {
+            if (name == null)
+                return null;
+            return (String)_headers.getValue(name.toLowerCase(Locale.ENGLISH), 0);
+        }
+
+        /** 
+         * @see javax.servlet.http.Part#getHeaderNames()
+         */
+        public Collection<String> getHeaderNames()
+        {
+            return _headers.keySet();
+        }
+
+        /** 
+         * @see javax.servlet.http.Part#getHeaders(java.lang.String)
+         */
+        public Collection<String> getHeaders(String name)
+        {
+           return _headers.getValues(name);
+        }
+
+        /** 
+         * @see javax.servlet.http.Part#getInputStream()
+         */
+        public InputStream getInputStream() throws IOException
+        {
+           if (_file != null)
+           {
+               //written to a file, whether temporary or not
+               return new BufferedInputStream (new FileInputStream(_file));
+           }
+           else
+           {
+               //part content is in memory
+               return new ByteArrayInputStream(_bout.getBuf(),0,_bout.size());
+           }
+        }
+
+        public byte[] getBytes()
+        {
+            if (_bout!=null)
+                return _bout.toByteArray();
+            return null;
+        }
+        
+        /** 
+         * @see javax.servlet.http.Part#getName()
+         */
+        public String getName()
+        {
+           return _name;
+        }
+
+        /** 
+         * @see javax.servlet.http.Part#getSize()
+         */
+        public long getSize()
+        {
+            return _size;         
+        }
+
+        /** 
+         * @see javax.servlet.http.Part#write(java.lang.String)
+         */
+        public void write(String fileName) throws IOException
+        {
+            if (_file == null)
+            {
+                _temporary = false;
+                
+                //part data is only in the ByteArrayOutputStream and never been written to disk
+                _file = new File (_tmpDir, fileName);
+
+                BufferedOutputStream bos = null;
+                try
+                {
+                    bos = new BufferedOutputStream(new FileOutputStream(_file));
+                    _bout.writeTo(bos);
+                    bos.flush();
+                }
+                finally
+                {
+                    if (bos != null)
+                        bos.close();
+                    _bout = null;
+                }
+            }
+            else
+            {
+                //the part data is already written to a temporary file, just rename it
+                _temporary = false;
+                
+                File f = new File(_tmpDir, fileName);
+                if (_file.renameTo(f))
+                    _file = f;
+            }
+        }
+        
+        /** 
+         * Remove the file, whether or not Part.write() was called on it
+         * (ie no longer temporary)
+         * @see javax.servlet.http.Part#delete()
+         */
+        public void delete() throws IOException
+        {
+            if (_file != null && _file.exists())
+                _file.delete();     
+        }
+        
+        /**
+         * Only remove tmp files.
+         * 
+         * @throws IOException
+         */
+        public void cleanUp() throws IOException
+        {
+            if (_temporary && _file != null && _file.exists())
+                _file.delete();
+        }
+        
+        
+        /**
+         * Get the file, if any, the data has been written to.
+         * @return
+         */
+        public File getFile ()
+        {
+            return _file;
+        }  
+        
+        
+        /**
+         * Get the filename from the content-disposition.
+         * @return null or the filename
+         */
+        public String getContentDispositionFilename ()
+        {
+            return _filename;
+        }
+    }
+    
+    
+    
+    
+    /**
+     * @param in Request input stream 
+     * @param contentType Content-Type header
+     * @param config MultipartConfigElement 
+     * @param contextTmpDir javax.servlet.context.tempdir
+     */
+    public MultiPartInputStream (InputStream in, String contentType, MultipartConfigElement config, File contextTmpDir)
+    {
+        _in = new ReadLineInputStream(in);
+       _contentType = contentType;
+       _config = config;
+       _contextTmpDir = contextTmpDir;
+       if (_contextTmpDir == null)
+           _contextTmpDir = new File (System.getProperty("java.io.tmpdir"));
+       
+       if (_config == null)
+           _config = new MultipartConfigElement(_contextTmpDir.getAbsolutePath());
+    }
+
+    /**
+     * Get the already parsed parts.
+     * 
+     * @return
+     */
+    public Collection<Part> getParsedParts()
+    {
+        if (_parts == null)
+            return Collections.emptyList();
+
+        Collection<Object> values = _parts.values();
+        List<Part> parts = new ArrayList<Part>();
+        for (Object o: values)
+        {
+            List<Part> asList = LazyList.getList(o, false);
+            parts.addAll(asList);
+        }
+        return parts;
+    }
+    
+    /**
+     * Delete any tmp storage for parts, and clear out the parts list.
+     * 
+     * @throws MultiException
+     */
+    public void deleteParts ()
+    throws MultiException
+    {
+        Collection<Part> parts = getParsedParts();
+        MultiException err = new MultiException();
+        for (Part p:parts)
+        {
+            try
+            {
+                ((MultiPartInputStream.MultiPart)p).cleanUp();
+            } 
+            catch(Exception e)
+            {     
+                err.add(e); 
+            }
+        }
+        _parts.clear();
+        
+        err.ifExceptionThrowMulti();
+    }
+
+   
+    /**
+     * Parse, if necessary, the multipart data and return the list of Parts.
+     * 
+     * @return
+     * @throws IOException
+     * @throws ServletException
+     */
+    public Collection<Part> getParts()
+    throws IOException, ServletException
+    {
+        parse();
+        Collection<Object> values = _parts.values();
+        List<Part> parts = new ArrayList<Part>();
+        for (Object o: values)
+        {
+            List<Part> asList = LazyList.getList(o, false);
+            parts.addAll(asList);
+        }
+        return parts;
+    }
+    
+    
+    /**
+     * Get the named Part.
+     * 
+     * @param name
+     * @return
+     * @throws IOException
+     * @throws ServletException
+     */
+    public Part getPart(String name)
+    throws IOException, ServletException
+    {
+        parse();
+        return (Part)_parts.getValue(name, 0);
+    }
+    
+    
+    /**
+     * Parse, if necessary, the multipart stream.
+     * 
+     * @throws IOException
+     * @throws ServletException
+     */
+    protected void parse ()
+    throws IOException, ServletException
+    {
+        //have we already parsed the input?
+        if (_parts != null)
+            return;
+        
+        //initialize
+        long total = 0; //keep running total of size of bytes read from input and throw an exception if exceeds MultipartConfigElement._maxRequestSize              
+        _parts = new MultiMap<String>();
+
+        //if its not a multipart request, don't parse it
+        if (_contentType == null || !_contentType.startsWith("multipart/form-data"))
+            return;
+ 
+        //sort out the location to which to write the files
+        
+        if (_config.getLocation() == null)
+            _tmpDir = _contextTmpDir;
+        else if ("".equals(_config.getLocation()))
+            _tmpDir = _contextTmpDir;
+        else
+        {
+            File f = new File (_config.getLocation());
+            if (f.isAbsolute())
+                _tmpDir = f;
+            else
+                _tmpDir = new File (_contextTmpDir, _config.getLocation());
+        }
+      
+        if (!_tmpDir.exists())
+            _tmpDir.mkdirs();
+
+        String contentTypeBoundary = "";
+        int bstart = _contentType.indexOf("boundary=");
+        if (bstart >= 0)
+        {
+            int bend = _contentType.indexOf(";", bstart);
+            bend = (bend < 0? _contentType.length(): bend);
+            contentTypeBoundary = QuotedStringTokenizer.unquote(value(_contentType.substring(bstart,bend), true).trim());
+        }
+        
+        String boundary="--"+contentTypeBoundary;
+        byte[] byteBoundary=(boundary+"--").getBytes(StringUtil.__ISO_8859_1);
+
+        // Get first boundary
+        String line = null;
+        try
+        {
+            line=((ReadLineInputStream)_in).readLine();  
+        }
+        catch (IOException e)
+        {
+            LOG.warn("Badly formatted multipart request");
+            throw e;
+        }
+
+        if (line == null)
+            throw new IOException("Missing content for multipart request");
+
+        boolean badFormatLogged = false;
+        line=line.trim();
+        while (line != null && !line.equals(boundary))
+        {
+            if (!badFormatLogged)
+            {
+                LOG.warn("Badly formatted multipart request");
+                badFormatLogged = true;
+            }
+            line=((ReadLineInputStream)_in).readLine();
+            line=(line==null?line:line.trim());
+        }
+
+        if (line == null)
+            throw new IOException("Missing initial multi part boundary");
+
+        // Read each part
+        boolean lastPart=false;
+
+        outer:while(!lastPart)
+        {
+            String contentDisposition=null;
+            String contentType=null;
+            String contentTransferEncoding=null;
+            
+            MultiMap<String> headers = new MultiMap<String>();
+            while(true)
+            {
+                line=((ReadLineInputStream)_in).readLine();
+                
+                //No more input
+                if(line==null)
+                    break outer;
+
+                // If blank line, end of part headers
+                if("".equals(line))
+                    break;
+                
+                total += line.length();
+                if (_config.getMaxRequestSize() > 0 && total > _config.getMaxRequestSize())
+                    throw new IllegalStateException ("Request exceeds maxRequestSize ("+_config.getMaxRequestSize()+")");
+
+                //get content-disposition and content-type
+                int c=line.indexOf(':',0);
+                if(c>0)
+                {
+                    String key=line.substring(0,c).trim().toLowerCase(Locale.ENGLISH);
+                    String value=line.substring(c+1,line.length()).trim();
+                    headers.put(key, value);
+                    if (key.equalsIgnoreCase("content-disposition"))
+                        contentDisposition=value;
+                    if (key.equalsIgnoreCase("content-type"))
+                        contentType = value;
+                    if(key.equals("content-transfer-encoding"))
+                        contentTransferEncoding=value;
+
+                }
+            }
+
+            // Extract content-disposition
+            boolean form_data=false;
+            if(contentDisposition==null)
+            {
+                throw new IOException("Missing content-disposition");
+            }
+
+            QuotedStringTokenizer tok=new QuotedStringTokenizer(contentDisposition,";", false, true);
+            String name=null;
+            String filename=null;
+            while(tok.hasMoreTokens())
+            {
+                String t=tok.nextToken().trim();
+                String tl=t.toLowerCase(Locale.ENGLISH);
+                if(t.startsWith("form-data"))
+                    form_data=true;
+                else if(tl.startsWith("name="))
+                    name=value(t, true);
+                else if(tl.startsWith("filename="))
+                    filename=filenameValue(t);
+            }
+
+            // Check disposition
+            if(!form_data)
+            {
+                continue;
+            }
+            //It is valid for reset and submit buttons to have an empty name.
+            //If no name is supplied, the browser skips sending the info for that field.
+            //However, if you supply the empty string as the name, the browser sends the
+            //field, with name as the empty string. So, only continue this loop if we
+            //have not yet seen a name field.
+            if(name==null)
+            {
+                continue;
+            }
+
+            //Have a new Part
+            MultiPart part = new MultiPart(name, filename);
+            part.setHeaders(headers);
+            part.setContentType(contentType);
+            _parts.add(name, part);
+            part.open();
+            
+            InputStream partInput = null;
+            if ("base64".equalsIgnoreCase(contentTransferEncoding))
+            {
+                partInput = new Base64InputStream((ReadLineInputStream)_in);
+            }
+            else if ("quoted-printable".equalsIgnoreCase(contentTransferEncoding))
+            {
+                partInput = new FilterInputStream(_in)
+                {
+                    @Override
+                    public int read() throws IOException
+                    {
+                        int c = in.read();
+                        if (c >= 0 && c == '=')
+                        {
+                            int hi = in.read();
+                            int lo = in.read();
+                            if (hi < 0 || lo < 0)
+                            {
+                                throw new IOException("Unexpected end to quoted-printable byte");
+                            }
+                            char[] chars = new char[] { (char)hi, (char)lo };
+                            c = Integer.parseInt(new String(chars),16);
+                        }
+                        return c;
+                    }
+                };
+            }
+            else
+                partInput = _in;
+            
+            try
+            { 
+                int state=-2;
+                int c;
+                boolean cr=false;
+                boolean lf=false;
+
+                // loop for all lines
+                while(true)
+                {
+                    int b=0;
+                    while((c=(state!=-2)?state:partInput.read())!=-1)
+                    {
+                        total ++;
+                        if (_config.getMaxRequestSize() > 0 && total > _config.getMaxRequestSize())
+                            throw new IllegalStateException("Request exceeds maxRequestSize ("+_config.getMaxRequestSize()+")");
+                        
+                        state=-2;
+                        
+                        // look for CR and/or LF
+                        if(c==13||c==10)
+                        {
+                            if(c==13)
+                            {
+                                partInput.mark(1);
+                                int tmp=partInput.read();
+                                if (tmp!=10)
+                                    partInput.reset();
+                                else
+                                    state=tmp;
+                            }
+                            break;
+                        }
+                        
+                        // Look for boundary
+                        if(b>=0&&b<byteBoundary.length&&c==byteBoundary[b])
+                        {
+                            b++;
+                        }
+                        else
+                        {
+                            // Got a character not part of the boundary, so we don't have the boundary marker.
+                            // Write out as many chars as we matched, then the char we're looking at.
+                            if(cr)
+                                part.write(13);
+                    
+                            if(lf)
+                                part.write(10); 
+                            
+                            cr=lf=false;
+                            if(b>0)
+                                part.write(byteBoundary,0,b);
+                              
+                            b=-1;
+                            part.write(c);
+                        }
+                    }
+                    
+                    // Check for incomplete boundary match, writing out the chars we matched along the way
+                    if((b>0&&b<byteBoundary.length-2)||(b==byteBoundary.length-1))
+                    {
+                        if(cr)
+                            part.write(13);
+
+                        if(lf)
+                            part.write(10);
+
+                        cr=lf=false;
+                        part.write(byteBoundary,0,b);
+                        b=-1;
+                    }
+                    
+                    // Boundary match. If we've run out of input or we matched the entire final boundary marker, then this is the last part.
+                    if(b>0||c==-1)
+                    {
+                       
+                        if(b==byteBoundary.length)
+                            lastPart=true;
+                        if(state==10)
+                            state=-2;
+                        break;
+                    }
+                    
+                    // handle CR LF
+                    if(cr)
+                        part.write(13); 
+
+                    if(lf)
+                        part.write(10);
+
+                    cr=(c==13);
+                    lf=(c==10||state==10);
+                    if(state==10)
+                        state=-2;
+                }
+            }
+            finally
+            {
+                part.close();
+            }
+        }
+        if (!lastPart)
+            throw new IOException("Incomplete parts");
+    }
+    
+    public void setDeleteOnExit(boolean deleteOnExit)
+    {
+        _deleteOnExit = deleteOnExit;
+    }
+
+
+    public boolean isDeleteOnExit()
+    {
+        return _deleteOnExit;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    private String value(String nameEqualsValue, boolean splitAfterSpace)
+    {
+        /*
+        String value=nameEqualsValue.substring(nameEqualsValue.indexOf('=')+1).trim();
+        int i=value.indexOf(';');
+        if(i>0)
+            value=value.substring(0,i);
+        if(value.startsWith("\""))
+        {
+            value=value.substring(1,value.indexOf('"',1));
+        }
+        else if (splitAfterSpace)
+        {
+            i=value.indexOf(' ');
+            if(i>0)
+                value=value.substring(0,i);
+        }
+        return value;
+        */
+         int idx = nameEqualsValue.indexOf('=');
+         String value = nameEqualsValue.substring(idx+1).trim();
+         return QuotedStringTokenizer.unquoteOnly(value);
+    }
+    
+    
+    /* ------------------------------------------------------------ */
+    private String filenameValue(String nameEqualsValue)
+    {
+        int idx = nameEqualsValue.indexOf('=');
+        String value = nameEqualsValue.substring(idx+1).trim();   
+
+        if (value.matches(".??[a-z,A-Z]\\:\\\\[^\\\\].*"))
+        {
+            //incorrectly escaped IE filenames that have the whole path
+            //we just strip any leading & trailing quotes and leave it as is
+            char first=value.charAt(0);
+            if (first=='"' || first=='\'')
+                value=value.substring(1);
+            char last=value.charAt(value.length()-1);
+            if (last=='"' || last=='\'')
+                value = value.substring(0,value.length()-1);
+
+            return value;
+        }
+        else
+            //unquote the string, but allow any backslashes that don't
+            //form a valid escape sequence to remain as many browsers
+            //even on *nix systems will not escape a filename containing
+            //backslashes
+            return QuotedStringTokenizer.unquoteOnly(value, true);
+    }
+    
+    private static class Base64InputStream extends InputStream
+    {
+        ReadLineInputStream _in;
+        String _line;
+        byte[] _buffer;
+        int _pos;
+
+    
+        public Base64InputStream(ReadLineInputStream rlis)
+        {
+            _in = rlis;
+        }
+
+        @Override
+        public int read() throws IOException
+        {
+            if (_buffer==null || _pos>= _buffer.length)
+            {
+                //Any CR and LF will be consumed by the readLine() call.
+                //We need to put them back into the bytes returned from this
+                //method because the parsing of the multipart content uses them
+                //as markers to determine when we've reached the end of a part.
+                _line = _in.readLine(); 
+                if (_line==null)
+                    return -1;  //nothing left
+                if (_line.startsWith("--"))
+                    _buffer=(_line+"\r\n").getBytes(); //boundary marking end of part
+                else if (_line.length()==0)
+                    _buffer="\r\n".getBytes(); //blank line
+                else
+                {
+                    ByteArrayOutputStream baos = new ByteArrayOutputStream((4*_line.length()/3)+2);
+                    B64Code.decode(_line, baos);
+                    baos.write(13);
+                    baos.write(10);
+                    _buffer = baos.toByteArray();
+                }
+
+                _pos=0;
+            }
+            
+            return _buffer[_pos++];
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/MultiPartOutputStream.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,140 @@
+//
+//  ========================================================================
+//  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.util;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+
+/* ================================================================ */
+/** Handle a multipart MIME response.
+ *
+ * 
+ * 
+*/
+public class MultiPartOutputStream extends FilterOutputStream
+{
+    /* ------------------------------------------------------------ */
+    private static final byte[] __CRLF={'\r','\n'};
+    private static final byte[] __DASHDASH={'-','-'};
+    
+    public static String MULTIPART_MIXED="multipart/mixed";
+    public static String MULTIPART_X_MIXED_REPLACE="multipart/x-mixed-replace";
+    
+    /* ------------------------------------------------------------ */
+    private String boundary;
+    private byte[] boundaryBytes;
+
+    /* ------------------------------------------------------------ */
+    private boolean inPart=false;    
+    
+    /* ------------------------------------------------------------ */
+    public MultiPartOutputStream(OutputStream out)
+    throws IOException
+    {
+        super(out);
+
+        boundary = "jetty"+System.identityHashCode(this)+
+        Long.toString(System.currentTimeMillis(),36);
+        boundaryBytes=boundary.getBytes(StringUtil.__ISO_8859_1);
+
+        inPart=false;
+    }
+
+    
+
+    /* ------------------------------------------------------------ */
+    /** End the current part.
+     * @exception IOException IOException
+     */
+    @Override
+    public void close()
+         throws IOException
+    {
+        if (inPart)
+            out.write(__CRLF);
+        out.write(__DASHDASH);
+        out.write(boundaryBytes);
+        out.write(__DASHDASH);
+        out.write(__CRLF);
+        inPart=false;
+        super.close();
+    }
+    
+    /* ------------------------------------------------------------ */
+    public String getBoundary()
+    {
+        return boundary;
+    }
+
+    public OutputStream getOut() {return out;}
+    
+    /* ------------------------------------------------------------ */
+    /** Start creation of the next Content.
+     */
+    public void startPart(String contentType)
+         throws IOException
+    {
+        if (inPart)
+            out.write(__CRLF);
+        inPart=true;
+        out.write(__DASHDASH);
+        out.write(boundaryBytes);
+        out.write(__CRLF);
+        if (contentType != null)
+            out.write(("Content-Type: "+contentType).getBytes(StringUtil.__ISO_8859_1));
+        out.write(__CRLF);
+        out.write(__CRLF);
+    }
+        
+    /* ------------------------------------------------------------ */
+    /** Start creation of the next Content.
+     */
+    public void startPart(String contentType, String[] headers)
+         throws IOException
+    {
+        if (inPart)
+            out.write(__CRLF);
+        inPart=true;
+        out.write(__DASHDASH);
+        out.write(boundaryBytes);
+        out.write(__CRLF);
+        if (contentType != null)
+            out.write(("Content-Type: "+contentType).getBytes(StringUtil.__ISO_8859_1));
+        out.write(__CRLF);
+        for (int i=0;headers!=null && i<headers.length;i++)
+        {
+            out.write(headers[i].getBytes(StringUtil.__ISO_8859_1));
+            out.write(__CRLF);
+        }
+        out.write(__CRLF);
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public void write(byte[] b, int off, int len) throws IOException
+    {
+        out.write(b,off,len);
+    }
+}
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/MultiPartWriter.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,138 @@
+//
+//  ========================================================================
+//  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.util;
+
+import java.io.FilterWriter;
+import java.io.IOException;
+import java.io.Writer;
+
+
+/* ================================================================ */
+/** Handle a multipart MIME response.
+ *
+ * 
+ * 
+*/
+public class MultiPartWriter extends FilterWriter
+{
+    /* ------------------------------------------------------------ */
+    private final static String __CRLF="\015\012";
+    private final static String __DASHDASH="--";
+    
+    public static String MULTIPART_MIXED=MultiPartOutputStream.MULTIPART_MIXED;
+    public static String MULTIPART_X_MIXED_REPLACE=MultiPartOutputStream.MULTIPART_X_MIXED_REPLACE;
+    
+    /* ------------------------------------------------------------ */
+    private String boundary;
+
+    /* ------------------------------------------------------------ */
+    private boolean inPart=false;    
+    
+    /* ------------------------------------------------------------ */
+    public MultiPartWriter(Writer out)
+         throws IOException
+    {
+        super(out);
+        boundary = "jetty"+System.identityHashCode(this)+
+        Long.toString(System.currentTimeMillis(),36);
+        
+        inPart=false;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** End the current part.
+     * @exception IOException IOException
+     */
+    @Override
+    public void close()
+         throws IOException
+    {
+        if (inPart)
+            out.write(__CRLF);
+        out.write(__DASHDASH);
+        out.write(boundary);
+        out.write(__DASHDASH);
+        out.write(__CRLF);
+        inPart=false;
+        super.close();
+    }
+    
+    /* ------------------------------------------------------------ */
+    public String getBoundary()
+    {
+        return boundary;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Start creation of the next Content.
+     */
+    public void startPart(String contentType)
+         throws IOException
+    {
+        if (inPart)
+            out.write(__CRLF);
+        out.write(__DASHDASH);
+        out.write(boundary);
+        out.write(__CRLF);
+        out.write("Content-Type: ");
+        out.write(contentType);
+        out.write(__CRLF);
+        out.write(__CRLF);
+        inPart=true;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** end creation of the next Content.
+     */
+    public void endPart()
+         throws IOException
+    {
+        if (inPart)
+            out.write(__CRLF);
+        inPart=false;
+    }
+        
+    /* ------------------------------------------------------------ */
+    /** Start creation of the next Content.
+     */
+    public void startPart(String contentType, String[] headers)
+         throws IOException
+    {
+        if (inPart)
+            out.write(__CRLF);
+        out.write(__DASHDASH);
+        out.write(boundary);
+        out.write(__CRLF);
+        out.write("Content-Type: ");
+        out.write(contentType);
+        out.write(__CRLF);
+        for (int i=0;headers!=null && i<headers.length;i++)
+        {
+            out.write(headers[i]);
+            out.write(__CRLF);
+        }
+        out.write(__CRLF);
+        inPart=true;
+    }
+    
+}
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/PatternMatcher.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,104 @@
+//
+//  ========================================================================
+//  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.util;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+public abstract class PatternMatcher
+{
+    public abstract void matched (URI uri) throws Exception;
+    
+    
+    /**
+     * Find jar names from the provided list matching a pattern.
+     * 
+     * If the pattern is null and isNullInclusive is true, then
+     * all jar names will match.
+     * 
+     * A pattern is a set of acceptable jar names. Each acceptable
+     * jar name is a regex. Each regex can be separated by either a
+     * "," or a "|". If you use a "|" this or's together the jar
+     * name patterns. This means that ordering of the matches is
+     * unimportant to you. If instead, you want to match particular
+     * jar names, and you want to match them in order, you should
+     * separate the regexs with "," instead. 
+     * 
+     * Eg "aaa-.*\\.jar|bbb-.*\\.jar"
+     * Will iterate over the jar names and match
+     * in any order.
+     * 
+     * Eg "aaa-*\\.jar,bbb-.*\\.jar"
+     * Will iterate over the jar names, matching
+     * all those starting with "aaa-" first, then "bbb-".
+     *
+     * @param pattern the pattern
+     * @param uris the uris to test the pattern against
+     * @param isNullInclusive if true, an empty pattern means all names match, if false, none match
+     * @throws Exception
+     */
+    public void match (Pattern pattern, URI[] uris, boolean isNullInclusive)
+    throws Exception
+    {
+        if (uris!=null)
+        {
+            String[] patterns = (pattern==null?null:pattern.pattern().split(","));
+
+            List<Pattern> subPatterns = new ArrayList<Pattern>();
+            for (int i=0; patterns!=null && i<patterns.length;i++)
+            {
+                subPatterns.add(Pattern.compile(patterns[i]));
+            }
+            if (subPatterns.isEmpty())
+                subPatterns.add(pattern);
+
+            if (subPatterns.isEmpty())
+            {
+                matchPatterns(null, uris, isNullInclusive);
+            }
+            else
+            {
+                //for each subpattern, iterate over all the urls, processing those that match
+                for (Pattern p : subPatterns)
+                {
+                    matchPatterns(p, uris, isNullInclusive);
+                }
+            }
+        }
+    }
+
+
+    public void matchPatterns (Pattern pattern, URI[] uris, boolean isNullInclusive)
+    throws Exception
+    {
+        for (int i=0; i<uris.length;i++)
+        {
+            URI uri = uris[i];
+            String s = uri.toString();
+            if ((pattern == null && isNullInclusive)
+                    ||
+                    (pattern!=null && pattern.matcher(s).matches()))
+            {
+                matched(uris[i]);
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/QuotedStringTokenizer.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,603 @@
+//
+//  ========================================================================
+//  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.util;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.NoSuchElementException;
+import java.util.StringTokenizer;
+
+/* ------------------------------------------------------------ */
+/** StringTokenizer with Quoting support.
+ *
+ * This class is a copy of the java.util.StringTokenizer API and
+ * the behaviour is the same, except that single and double quoted
+ * string values are recognised.
+ * Delimiters within quotes are not considered delimiters.
+ * Quotes can be escaped with '\'.
+ *
+ * @see java.util.StringTokenizer
+ *
+ */
+public class QuotedStringTokenizer
+    extends StringTokenizer
+{
+    private final static String __delim="\t\n\r";
+    private String _string;
+    private String _delim = __delim;
+    private boolean _returnQuotes=false;
+    private boolean _returnDelimiters=false;
+    private StringBuffer _token;
+    private boolean _hasToken=false;
+    private int _i=0;
+    private int _lastStart=0;
+    private boolean _double=true;
+    private boolean _single=true;
+
+    /* ------------------------------------------------------------ */
+    public QuotedStringTokenizer(String str,
+                                 String delim,
+                                 boolean returnDelimiters,
+                                 boolean returnQuotes)
+    {
+        super("");
+        _string=str;
+        if (delim!=null)
+            _delim=delim;
+        _returnDelimiters=returnDelimiters;
+        _returnQuotes=returnQuotes;
+
+        if (_delim.indexOf('\'')>=0 ||
+            _delim.indexOf('"')>=0)
+            throw new Error("Can't use quotes as delimiters: "+_delim);
+
+        _token=new StringBuffer(_string.length()>1024?512:_string.length()/2);
+    }
+
+    /* ------------------------------------------------------------ */
+    public QuotedStringTokenizer(String str,
+                                 String delim,
+                                 boolean returnDelimiters)
+    {
+        this(str,delim,returnDelimiters,false);
+    }
+
+    /* ------------------------------------------------------------ */
+    public QuotedStringTokenizer(String str,
+                                 String delim)
+    {
+        this(str,delim,false,false);
+    }
+
+    /* ------------------------------------------------------------ */
+    public QuotedStringTokenizer(String str)
+    {
+        this(str,null,false,false);
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public boolean hasMoreTokens()
+    {
+        // Already found a token
+        if (_hasToken)
+            return true;
+
+        _lastStart=_i;
+
+        int state=0;
+        boolean escape=false;
+        while (_i<_string.length())
+        {
+            char c=_string.charAt(_i++);
+
+            switch (state)
+            {
+              case 0: // Start
+                  if(_delim.indexOf(c)>=0)
+                  {
+                      if (_returnDelimiters)
+                      {
+                          _token.append(c);
+                          return _hasToken=true;
+                      }
+                  }
+                  else if (c=='\'' && _single)
+                  {
+                      if (_returnQuotes)
+                          _token.append(c);
+                      state=2;
+                  }
+                  else if (c=='\"' && _double)
+                  {
+                      if (_returnQuotes)
+                          _token.append(c);
+                      state=3;
+                  }
+                  else
+                  {
+                      _token.append(c);
+                      _hasToken=true;
+                      state=1;
+                  }
+                  break;
+
+              case 1: // Token
+                  _hasToken=true;
+                  if(_delim.indexOf(c)>=0)
+                  {
+                      if (_returnDelimiters)
+                          _i--;
+                      return _hasToken;
+                  }
+                  else if (c=='\'' && _single)
+                  {
+                      if (_returnQuotes)
+                          _token.append(c);
+                      state=2;
+                  }
+                  else if (c=='\"' && _double)
+                  {
+                      if (_returnQuotes)
+                          _token.append(c);
+                      state=3;
+                  }
+                  else
+                  {
+                      _token.append(c);
+                  }
+                  break;
+
+              case 2: // Single Quote
+                  _hasToken=true;
+                  if (escape)
+                  {
+                      escape=false;
+                      _token.append(c);
+                  }
+                  else if (c=='\'')
+                  {
+                      if (_returnQuotes)
+                          _token.append(c);
+                      state=1;
+                  }
+                  else if (c=='\\')
+                  {
+                      if (_returnQuotes)
+                          _token.append(c);
+                      escape=true;
+                  }
+                  else
+                  {
+                      _token.append(c);
+                  }
+                  break;
+
+              case 3: // Double Quote
+                  _hasToken=true;
+                  if (escape)
+                  {
+                      escape=false;
+                      _token.append(c);
+                  }
+                  else if (c=='\"')
+                  {
+                      if (_returnQuotes)
+                          _token.append(c);
+                      state=1;
+                  }
+                  else if (c=='\\')
+                  {
+                      if (_returnQuotes)
+                          _token.append(c);
+                      escape=true;
+                  }
+                  else
+                  {
+                      _token.append(c);
+                  }
+                  break;
+            }
+        }
+
+        return _hasToken;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public String nextToken()
+        throws NoSuchElementException
+    {
+        if (!hasMoreTokens() || _token==null)
+            throw new NoSuchElementException();
+        String t=_token.toString();
+        _token.setLength(0);
+        _hasToken=false;
+        return t;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public String nextToken(String delim)
+        throws NoSuchElementException
+    {
+        _delim=delim;
+        _i=_lastStart;
+        _token.setLength(0);
+        _hasToken=false;
+        return nextToken();
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public boolean hasMoreElements()
+    {
+        return hasMoreTokens();
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public Object nextElement()
+        throws NoSuchElementException
+    {
+        return nextToken();
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Not implemented.
+     */
+    @Override
+    public int countTokens()
+    {
+        return -1;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /** Quote a string.
+     * The string is quoted only if quoting is required due to
+     * embedded delimiters, quote characters or the
+     * empty string.
+     * @param s The string to quote.
+     * @param delim the delimiter to use to quote the string
+     * @return quoted string
+     */
+    public static String quoteIfNeeded(String s, String delim)
+    {
+        if (s==null)
+            return null;
+        if (s.length()==0)
+            return "\"\"";
+
+
+        for (int i=0;i<s.length();i++)
+        {
+            char c = s.charAt(i);
+            if (c=='\\' || c=='"' || c=='\'' || Character.isWhitespace(c) || delim.indexOf(c)>=0)
+            {
+                StringBuffer b=new StringBuffer(s.length()+8);
+                quote(b,s);
+                return b.toString();
+            }
+        }
+
+        return s;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Quote a string.
+     * The string is quoted only if quoting is required due to
+     * embeded delimiters, quote characters or the
+     * empty string.
+     * @param s The string to quote.
+     * @return quoted string
+     */
+    public static String quote(String s)
+    {
+        if (s==null)
+            return null;
+        if (s.length()==0)
+            return "\"\"";
+
+        StringBuffer b=new StringBuffer(s.length()+8);
+        quote(b,s);
+        return b.toString();
+
+    }
+
+    private static final char[] escapes = new char[32];
+    static
+    {
+        Arrays.fill(escapes, (char)0xFFFF);
+        escapes['\b'] = 'b';
+        escapes['\t'] = 't';
+        escapes['\n'] = 'n';
+        escapes['\f'] = 'f';
+        escapes['\r'] = 'r';
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Quote a string into an Appendable.
+     * The characters ", \, \n, \r, \t, \f and \b are escaped
+     * @param buffer The Appendable
+     * @param input The String to quote.
+     */
+    public static void quote(Appendable buffer, String input)
+    {
+        try
+        {
+            buffer.append('"');
+            for (int i = 0; i < input.length(); ++i)
+            {
+                char c = input.charAt(i);
+                if (c >= 32)
+                {
+                    if (c == '"' || c == '\\')
+                        buffer.append('\\');
+                    buffer.append(c);
+                }
+                else
+                {
+                    char escape = escapes[c];
+                    if (escape == 0xFFFF)
+                    {
+                        // Unicode escape
+                        buffer.append('\\').append('u').append('0').append('0');
+                        if (c < 0x10)
+                            buffer.append('0');
+                        buffer.append(Integer.toString(c, 16));
+                    }
+                    else
+                    {
+                        buffer.append('\\').append(escape);
+                    }
+                }
+            }
+            buffer.append('"');
+        }
+        catch (IOException x)
+        {
+            throw new RuntimeException(x);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Quote a string into a StringBuffer only if needed.
+     * Quotes are forced if any delim characters are present.
+     *
+     * @param buf The StringBuffer
+     * @param s The String to quote.
+     * @param delim String of characters that must be quoted.
+     * @return true if quoted;
+     */
+    public static boolean quoteIfNeeded(Appendable buf, String s,String delim)
+    {
+        for (int i=0;i<s.length();i++)
+        {
+            char c = s.charAt(i);
+            if (delim.indexOf(c)>=0)
+            {
+            	quote(buf,s);
+            	return true;
+            }
+        }
+
+        try
+        {
+            buf.append(s);
+            return false;
+        }
+        catch(IOException e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+    
+    
+    /* ------------------------------------------------------------ */
+    public static String unquoteOnly(String s)
+    {
+        return unquoteOnly(s, false);
+    }
+    
+    
+    /* ------------------------------------------------------------ */
+    /** Unquote a string, NOT converting unicode sequences
+     * @param s The string to unquote.
+     * @param lenient if true, will leave in backslashes that aren't valid escapes
+     * @return quoted string
+     */
+    public static String unquoteOnly(String s, boolean lenient)
+    {
+        if (s==null)
+            return null;
+        if (s.length()<2)
+            return s;
+
+        char first=s.charAt(0);
+        char last=s.charAt(s.length()-1);
+        if (first!=last || (first!='"' && first!='\''))
+            return s;
+
+        StringBuilder b = new StringBuilder(s.length() - 2);
+        boolean escape=false;
+        for (int i=1;i<s.length()-1;i++)
+        {
+            char c = s.charAt(i);
+
+            if (escape)
+            {
+                escape=false;
+                if (lenient && !isValidEscaping(c))
+                {
+                    b.append('\\');
+                }
+                b.append(c);
+            }
+            else if (c=='\\')
+            {
+                escape=true;
+            }
+            else
+            {
+                b.append(c);
+            }
+        }
+
+        return b.toString(); 
+    }
+    
+    /* ------------------------------------------------------------ */
+    public static String unquote(String s)
+    {
+        return unquote(s,false);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Unquote a string.
+     * @param s The string to unquote.
+     * @return quoted string
+     */
+    public static String unquote(String s, boolean lenient)
+    {
+        if (s==null)
+            return null;
+        if (s.length()<2)
+            return s;
+
+        char first=s.charAt(0);
+        char last=s.charAt(s.length()-1);
+        if (first!=last || (first!='"' && first!='\''))
+            return s;
+
+        StringBuilder b = new StringBuilder(s.length() - 2);
+        boolean escape=false;
+        for (int i=1;i<s.length()-1;i++)
+        {
+            char c = s.charAt(i);
+
+            if (escape)
+            {
+                escape=false;
+                switch (c)
+                {
+                    case 'n':
+                        b.append('\n');
+                        break;
+                    case 'r':
+                        b.append('\r');
+                        break;
+                    case 't':
+                        b.append('\t');
+                        break;
+                    case 'f':
+                        b.append('\f');
+                        break;
+                    case 'b':
+                        b.append('\b');
+                        break;
+                    case '\\':
+                        b.append('\\');
+                        break;
+                    case '/':
+                        b.append('/');
+                        break;
+                    case '"':
+                        b.append('"');
+                        break;
+                    case 'u':
+                        b.append((char)(
+                                (TypeUtil.convertHexDigit((byte)s.charAt(i++))<<24)+
+                                (TypeUtil.convertHexDigit((byte)s.charAt(i++))<<16)+
+                                (TypeUtil.convertHexDigit((byte)s.charAt(i++))<<8)+
+                                (TypeUtil.convertHexDigit((byte)s.charAt(i++)))
+                                )
+                        );
+                        break;
+                    default:
+                        if (lenient && !isValidEscaping(c))
+                        {
+                            b.append('\\');
+                        }
+                        b.append(c);
+                }
+            }
+            else if (c=='\\')
+            {
+                escape=true;
+            }
+            else
+            {
+                b.append(c);
+            }
+        }
+
+        return b.toString();
+    }
+    
+    
+    /* ------------------------------------------------------------ */
+    /** Check that char c (which is preceded by a backslash) is a valid
+     * escape sequence.
+     * @param c
+     * @return
+     */
+    private static boolean isValidEscaping(char c)
+    {
+        return ((c == 'n') || (c == 'r') || (c == 't') || 
+                 (c == 'f') || (c == 'b') || (c == '\\') || 
+                 (c == '/') || (c == '"') || (c == 'u'));
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return handle double quotes if true
+     */
+    public boolean getDouble()
+    {
+        return _double;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param d handle double quotes if true
+     */
+    public void setDouble(boolean d)
+    {
+        _double=d;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return handle single quotes if true
+     */
+    public boolean getSingle()
+    {
+        return _single;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param single handle single quotes if true
+     */
+    public void setSingle(boolean single)
+    {
+        _single=single;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/ReadLineInputStream.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,136 @@
+//
+//  ========================================================================
+//  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.util;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * ReadLineInputStream
+ *
+ * Read from an input stream, accepting CR/LF, LF or just CR.
+ */
+public class ReadLineInputStream extends BufferedInputStream
+{
+    boolean _seenCRLF;
+    boolean _skipLF;
+    
+    public ReadLineInputStream(InputStream in)
+    {
+        super(in);
+    }
+
+    public ReadLineInputStream(InputStream in, int size)
+    {
+        super(in,size);
+    }
+    
+    public String readLine() throws IOException
+    {
+        mark(buf.length);
+                
+        while (true)
+        {
+            int b=super.read();
+            
+            if (markpos < 0)
+                throw new IOException("Buffer size exceeded: no line terminator");
+            
+            if (b==-1)
+            {
+                int m=markpos;
+                markpos=-1;
+                if (pos>m)
+                    return new String(buf,m,pos-m,StringUtil.__UTF8_CHARSET);
+
+                return null;
+            }
+            
+            if (b=='\r')
+            {
+                int p=pos;
+                
+                // if we have seen CRLF before, hungrily consume LF
+                if (_seenCRLF && pos<count)
+                {
+                    if (buf[pos]=='\n')
+                        pos+=1;
+                }
+                else
+                    _skipLF=true;
+                int m=markpos;
+                markpos=-1;
+                return new String(buf,m,p-m-1,StringUtil.__UTF8_CHARSET);
+            }
+            
+            if (b=='\n')
+            {
+                if (_skipLF)
+                {
+                    _skipLF=false;
+                    _seenCRLF=true;
+                    markpos++;
+                    continue;
+                }
+                int m=markpos;
+                markpos=-1;
+                return new String(buf,m,pos-m-1,StringUtil.__UTF8_CHARSET);
+            }
+        }
+    }
+
+    @Override
+    public synchronized int read() throws IOException
+    {
+        int b = super.read();
+        if (_skipLF)
+        {
+            _skipLF=false;
+            if (_seenCRLF && b=='\n')
+                b=super.read();
+        }
+        return b;
+    }
+
+    @Override
+    public synchronized int read(byte[] buf, int off, int len) throws IOException
+    {
+        if (_skipLF && len>0)
+        {
+            _skipLF=false;
+            if (_seenCRLF)
+            {
+                int b = super.read();
+                if (b==-1)
+                    return -1;
+                
+                if (b!='\n')
+                {
+                    buf[off]=(byte)(0xff&b);
+                    return 1+super.read(buf,off+1,len-1);
+                }
+            }
+        }
+        
+        return super.read(buf,off,len);
+    }
+    
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/RolloverFileOutputStream.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,340 @@
+//
+//  ========================================================================
+//  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.util; 
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Locale;
+import java.util.TimeZone;
+import java.util.Timer;
+import java.util.TimerTask;
+
+/** 
+ * RolloverFileOutputStream
+ * 
+ * This output stream puts content in a file that is rolled over every 24 hours.
+ * The filename must include the string "yyyy_mm_dd", which is replaced with the 
+ * actual date when creating and rolling over the file.
+ * 
+ * Old files are retained for a number of days before being deleted.
+ * 
+ * 
+ */
+public class RolloverFileOutputStream extends FilterOutputStream
+{
+    private static Timer __rollover;
+    
+    final static String YYYY_MM_DD="yyyy_mm_dd";
+    final static String ROLLOVER_FILE_DATE_FORMAT = "yyyy_MM_dd";
+    final static String ROLLOVER_FILE_BACKUP_FORMAT = "HHmmssSSS";
+    final static int ROLLOVER_FILE_RETAIN_DAYS = 31;
+
+    private RollTask _rollTask;
+    private SimpleDateFormat _fileBackupFormat;
+    private SimpleDateFormat _fileDateFormat;
+
+    private String _filename;
+    private File _file;
+    private boolean _append;
+    private int _retainDays;
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @param filename The filename must include the string "yyyy_mm_dd", 
+     * which is replaced with the actual date when creating and rolling over the file.
+     * @throws IOException
+     */
+    public RolloverFileOutputStream(String filename)
+        throws IOException
+    {
+        this(filename,true,ROLLOVER_FILE_RETAIN_DAYS);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @param filename The filename must include the string "yyyy_mm_dd", 
+     * which is replaced with the actual date when creating and rolling over the file.
+     * @param append If true, existing files will be appended to.
+     * @throws IOException
+     */
+    public RolloverFileOutputStream(String filename, boolean append)
+        throws IOException
+    {
+        this(filename,append,ROLLOVER_FILE_RETAIN_DAYS);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param filename The filename must include the string "yyyy_mm_dd", 
+     * which is replaced with the actual date when creating and rolling over the file.
+     * @param append If true, existing files will be appended to.
+     * @param retainDays The number of days to retain files before deleting them.  0 to retain forever.
+     * @throws IOException
+     */
+    public RolloverFileOutputStream(String filename,
+                                    boolean append,
+                                    int retainDays)
+        throws IOException
+    {
+        this(filename,append,retainDays,TimeZone.getDefault());
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param filename The filename must include the string "yyyy_mm_dd", 
+     * which is replaced with the actual date when creating and rolling over the file.
+     * @param append If true, existing files will be appended to.
+     * @param retainDays The number of days to retain files before deleting them. 0 to retain forever.
+     * @throws IOException
+     */
+    public RolloverFileOutputStream(String filename,
+                                    boolean append,
+                                    int retainDays,
+                                    TimeZone zone)
+        throws IOException
+    {
+
+         this(filename,append,retainDays,zone,null,null);
+    }
+     
+    /* ------------------------------------------------------------ */
+    /**
+     * @param filename The filename must include the string "yyyy_mm_dd", 
+     * which is replaced with the actual date when creating and rolling over the file.
+     * @param append If true, existing files will be appended to.
+     * @param retainDays The number of days to retain files before deleting them. 0 to retain forever.
+     * @param dateFormat The format for the date file substitution. The default is "yyyy_MM_dd". 
+     * @param backupFormat The format for the file extension of backup files. The default is "HHmmssSSS". 
+     * @throws IOException
+     */
+    public RolloverFileOutputStream(String filename,
+                                    boolean append,
+                                    int retainDays,
+                                    TimeZone zone,
+                                    String dateFormat,
+                                    String backupFormat)
+        throws IOException
+    {
+        super(null);
+
+        if (dateFormat==null)
+            dateFormat=ROLLOVER_FILE_DATE_FORMAT;
+        _fileDateFormat = new SimpleDateFormat(dateFormat);
+        
+        if (backupFormat==null)
+            backupFormat=ROLLOVER_FILE_BACKUP_FORMAT;
+        _fileBackupFormat = new SimpleDateFormat(backupFormat);
+        
+        _fileBackupFormat.setTimeZone(zone);
+        _fileDateFormat.setTimeZone(zone);
+        
+        if (filename!=null)
+        {
+            filename=filename.trim();
+            if (filename.length()==0)
+                filename=null;
+        }
+        if (filename==null)
+            throw new IllegalArgumentException("Invalid filename");
+
+        _filename=filename;
+        _append=append;
+        _retainDays=retainDays;
+        setFile();
+        
+        synchronized(RolloverFileOutputStream.class)
+        {
+            if (__rollover==null)
+                __rollover=new Timer(RolloverFileOutputStream.class.getName(),true);
+            
+            _rollTask=new RollTask();
+
+             Calendar now = Calendar.getInstance();
+             now.setTimeZone(zone);
+
+             GregorianCalendar midnight =
+                 new GregorianCalendar(now.get(Calendar.YEAR),
+                         now.get(Calendar.MONTH),
+                         now.get(Calendar.DAY_OF_MONTH),
+                         23,0);
+             midnight.setTimeZone(zone);
+             midnight.add(Calendar.HOUR,1);
+             __rollover.scheduleAtFixedRate(_rollTask,midnight.getTime(),1000L*60*60*24);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public String getFilename()
+    {
+        return _filename;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public String getDatedFilename()
+    {
+        if (_file==null)
+            return null;
+        return _file.toString();
+    }
+    
+    /* ------------------------------------------------------------ */
+    public int getRetainDays()
+    {
+        return _retainDays;
+    }
+
+    /* ------------------------------------------------------------ */
+    private synchronized void setFile()
+        throws IOException
+    {
+        // Check directory
+        File file = new File(_filename);
+        _filename=file.getCanonicalPath();
+        file=new File(_filename);
+        File dir= new File(file.getParent());
+        if (!dir.isDirectory() || !dir.canWrite())
+            throw new IOException("Cannot write log directory "+dir);
+            
+        Date now=new Date();
+        
+        // Is this a rollover file?
+        String filename=file.getName();
+        int i=filename.toLowerCase(Locale.ENGLISH).indexOf(YYYY_MM_DD);
+        if (i>=0)
+        {
+            file=new File(dir,
+                          filename.substring(0,i)+
+                          _fileDateFormat.format(now)+
+                          filename.substring(i+YYYY_MM_DD.length()));
+        }
+            
+        if (file.exists()&&!file.canWrite())
+            throw new IOException("Cannot write log file "+file);
+
+        // Do we need to change the output stream?
+        if (out==null || !file.equals(_file))
+        {
+            // Yep
+            _file=file;
+            if (!_append && file.exists())
+                file.renameTo(new File(file.toString()+"."+_fileBackupFormat.format(now)));
+            OutputStream oldOut=out;
+            out=new FileOutputStream(file.toString(),_append);
+            if (oldOut!=null)
+                oldOut.close();
+            //if(log.isDebugEnabled())log.debug("Opened "+_file);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    private void removeOldFiles()
+    {
+        if (_retainDays>0)
+        {
+            long now = System.currentTimeMillis();
+            
+            File file= new File(_filename);
+            File dir = new File(file.getParent());
+            String fn=file.getName();
+            int s=fn.toLowerCase(Locale.ENGLISH).indexOf(YYYY_MM_DD);
+            if (s<0)
+                return;
+            String prefix=fn.substring(0,s);
+            String suffix=fn.substring(s+YYYY_MM_DD.length());
+
+            String[] logList=dir.list();
+            for (int i=0;i<logList.length;i++)
+            {
+                fn = logList[i];
+                if(fn.startsWith(prefix)&&fn.indexOf(suffix,prefix.length())>=0)
+                {        
+                    File f = new File(dir,fn);
+                    long date = f.lastModified();
+                    if ( ((now-date)/(1000*60*60*24))>_retainDays)
+                        f.delete();   
+                }
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void write (byte[] buf)
+            throws IOException
+     {
+            out.write (buf);
+     }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void write (byte[] buf, int off, int len)
+            throws IOException
+     {
+            out.write (buf, off, len);
+     }
+    
+    /* ------------------------------------------------------------ */
+    /** 
+     */
+    @Override
+    public void close()
+        throws IOException
+    {
+        synchronized(RolloverFileOutputStream.class)
+        {
+            try{super.close();}
+            finally
+            {
+                out=null;
+                _file=null;
+            }
+
+            _rollTask.cancel(); 
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    private class RollTask extends TimerTask
+    {
+        @Override
+        public void run()
+        {
+            try
+            {
+                RolloverFileOutputStream.this.setFile();
+                RolloverFileOutputStream.this.removeOldFiles();
+
+            }
+            catch(IOException e)
+            {
+                // Cannot log this exception to a LOG, as RolloverFOS can be used by logging
+                e.printStackTrace();
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/Scanner.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,746 @@
+//
+//  ========================================================================
+//  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.util;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import org.eclipse.jetty.util.component.AbstractLifeCycle;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+
+/**
+ * Scanner
+ * 
+ * Utility for scanning a directory for added, removed and changed
+ * files and reporting these events via registered Listeners.
+ *
+ */
+public class Scanner extends AbstractLifeCycle
+{
+    private static final Logger LOG = Log.getLogger(Scanner.class);
+    private static int __scannerId=0;
+    private int _scanInterval;
+    private int _scanCount = 0;
+    private final List<Listener> _listeners = new ArrayList<Listener>();
+    private final Map<String,TimeNSize> _prevScan = new HashMap<String,TimeNSize> ();
+    private final Map<String,TimeNSize> _currentScan = new HashMap<String,TimeNSize> ();
+    private FilenameFilter _filter;
+    private final List<File> _scanDirs = new ArrayList<File>();
+    private volatile boolean _running = false;
+    private boolean _reportExisting = true;
+    private boolean _reportDirs = true;
+    private Timer _timer;
+    private TimerTask _task;
+    private int _scanDepth=0;
+    
+    public enum Notification { ADDED, CHANGED, REMOVED };
+    private final Map<String,Notification> _notifications = new HashMap<String,Notification>();
+
+    static class TimeNSize
+    {
+        final long _lastModified;
+        final long _size;
+        
+        public TimeNSize(long lastModified, long size)
+        {
+            _lastModified = lastModified;
+            _size = size;
+        }
+        
+        @Override
+        public int hashCode()
+        {
+            return (int)_lastModified^(int)_size;
+        }
+        
+        @Override
+        public boolean equals(Object o)
+        {
+            if (o instanceof TimeNSize)
+            {
+                TimeNSize tns = (TimeNSize)o;
+                return tns._lastModified==_lastModified && tns._size==_size;
+            }
+            return false;
+        }
+        
+        @Override
+        public String toString()
+        {
+            return "[lm="+_lastModified+",s="+_size+"]";
+        }
+    }
+    
+    /**
+     * Listener
+     * 
+     * Marker for notifications re file changes.
+     */
+    public interface Listener
+    {
+    }
+
+    public interface ScanListener extends Listener
+    {
+        public void scan();
+    }
+    
+    public interface DiscreteListener extends Listener
+    {
+        public void fileChanged (String filename) throws Exception;
+        public void fileAdded (String filename) throws Exception;
+        public void fileRemoved (String filename) throws Exception;
+    }
+    
+    
+    public interface BulkListener extends Listener
+    {
+        public void filesChanged (List<String> filenames) throws Exception;
+    }
+
+    /**
+     * Listener that notifies when a scan has started and when it has ended.
+     */
+    public interface ScanCycleListener extends Listener
+    {
+        public void scanStarted(int cycle) throws Exception;
+        public void scanEnded(int cycle) throws Exception;
+    }
+
+    /**
+     * 
+     */
+    public Scanner ()
+    {       
+    }
+
+    /**
+     * Get the scan interval
+     * @return interval between scans in seconds
+     */
+    public int getScanInterval()
+    {
+        return _scanInterval;
+    }
+
+    /**
+     * Set the scan interval
+     * @param scanInterval pause between scans in seconds, or 0 for no scan after the initial scan.
+     */
+    public synchronized void setScanInterval(int scanInterval)
+    {
+        _scanInterval = scanInterval;
+        schedule();
+    }
+
+    /**
+     * Set the location of the directory to scan.
+     * @param dir
+     * @deprecated use setScanDirs(List dirs) instead
+     */
+    @Deprecated
+    public void setScanDir (File dir)
+    {
+        _scanDirs.clear(); 
+        _scanDirs.add(dir);
+    }
+
+    /**
+     * Get the location of the directory to scan
+     * @return the first directory (of {@link #getScanDirs()} being scanned)
+     * @deprecated use getScanDirs() instead
+     */
+    @Deprecated
+    public File getScanDir ()
+    {
+        return (_scanDirs==null?null:(File)_scanDirs.get(0));
+    }
+
+    public void setScanDirs (List<File> dirs)
+    {
+        _scanDirs.clear(); 
+        _scanDirs.addAll(dirs);
+    }
+    
+    public synchronized void addScanDir( File dir )
+    {
+        _scanDirs.add( dir );
+    }
+    
+    public List<File> getScanDirs ()
+    {
+        return Collections.unmodifiableList(_scanDirs);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @param recursive True if scanning is recursive
+     * @see  #setScanDepth(int)
+     */
+    public void setRecursive (boolean recursive)
+    {
+        _scanDepth=recursive?-1:0;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @return True if scanning is fully recursive (scandepth==-1)
+     * @see #getScanDepth()
+     */
+    public boolean getRecursive ()
+    {
+        return _scanDepth==-1;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Get the scanDepth.
+     * @return the scanDepth
+     */
+    public int getScanDepth()
+    {
+        return _scanDepth;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set the scanDepth.
+     * @param scanDepth the scanDepth to set
+     */
+    public void setScanDepth(int scanDepth)
+    {
+        _scanDepth = scanDepth;
+    }
+
+    /**
+     * Apply a filter to files found in the scan directory.
+     * Only files matching the filter will be reported as added/changed/removed.
+     * @param filter
+     */
+    public void setFilenameFilter (FilenameFilter filter)
+    {
+        _filter = filter;
+    }
+
+    /**
+     * Get any filter applied to files in the scan dir.
+     * @return the filename filter
+     */
+    public FilenameFilter getFilenameFilter ()
+    {
+        return _filter;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Whether or not an initial scan will report all files as being
+     * added.
+     * @param reportExisting if true, all files found on initial scan will be 
+     * reported as being added, otherwise not
+     */
+    public void setReportExistingFilesOnStartup (boolean reportExisting)
+    {
+        _reportExisting = reportExisting;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean getReportExistingFilesOnStartup()
+    {
+        return _reportExisting;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Set if found directories should be reported.
+     * @param dirs
+     */
+    public void setReportDirs(boolean dirs)
+    {
+        _reportDirs=dirs;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public boolean getReportDirs()
+    {
+        return _reportDirs;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Add an added/removed/changed listener
+     * @param listener
+     */
+    public synchronized void addListener (Listener listener)
+    {
+        if (listener == null)
+            return;
+        _listeners.add(listener);   
+    }
+
+
+
+    /**
+     * Remove a registered listener
+     * @param listener the Listener to be removed
+     */
+    public synchronized void removeListener (Listener listener)
+    {
+        if (listener == null)
+            return;
+        _listeners.remove(listener);    
+    }
+
+
+    /**
+     * Start the scanning action.
+     */
+    @Override
+    public synchronized void doStart()
+    {
+        if (_running)
+            return;
+
+        _running = true;
+
+        if (_reportExisting)
+        {
+            // if files exist at startup, report them
+            scan();
+            scan(); // scan twice so files reported as stable
+        }
+        else
+        {
+            //just register the list of existing files and only report changes
+            scanFiles();
+            _prevScan.putAll(_currentScan);
+        }
+        schedule();
+    }
+
+    public TimerTask newTimerTask ()
+    {
+        return new TimerTask()
+        {
+            @Override
+            public void run() { scan(); }
+        };
+    }
+
+    public Timer newTimer ()
+    {
+        return new Timer("Scanner-"+__scannerId++, true);
+    }
+    
+    public void schedule ()
+    {  
+        if (_running)
+        {
+            if (_timer!=null)
+                _timer.cancel();
+            if (_task!=null)
+                _task.cancel();
+            if (getScanInterval() > 0)
+            {
+                _timer = newTimer();
+                _task = newTimerTask();
+                _timer.schedule(_task, 1010L*getScanInterval(),1010L*getScanInterval());
+            }
+        }
+    }
+    /**
+     * Stop the scanning.
+     */
+    @Override
+    public synchronized void doStop()
+    {
+        if (_running)
+        {
+            _running = false; 
+            if (_timer!=null)
+                _timer.cancel();
+            if (_task!=null)
+                _task.cancel();
+            _task=null;
+            _timer=null;
+        }
+    }
+
+    /**
+     * Perform a pass of the scanner and report changes
+     */
+    public synchronized void scan ()
+    {
+        reportScanStart(++_scanCount);
+        scanFiles();
+        reportDifferences(_currentScan, _prevScan);
+        _prevScan.clear();
+        _prevScan.putAll(_currentScan);
+        reportScanEnd(_scanCount);
+        
+        for (Listener l : _listeners)
+        {
+            try
+            {
+                if (l instanceof ScanListener)
+                    ((ScanListener)l).scan();
+            }
+            catch (Exception e)
+            {
+                LOG.warn(e);
+            }
+            catch (Error e)
+            {
+                LOG.warn(e);
+            }
+        }
+    }
+
+    /**
+     * Recursively scan all files in the designated directories.
+     */
+    public synchronized void scanFiles ()
+    {
+        if (_scanDirs==null)
+            return;
+        
+        _currentScan.clear();
+        Iterator<File> itor = _scanDirs.iterator();
+        while (itor.hasNext())
+        {
+            File dir = itor.next();
+            
+            if ((dir != null) && (dir.exists()))
+                try
+                {
+                    scanFile(dir.getCanonicalFile(), _currentScan,0);
+                }
+                catch (IOException e)
+                {
+                    LOG.warn("Error scanning files.", e);
+                }
+        }
+    }
+
+
+    /**
+     * Report the adds/changes/removes to the registered listeners
+     * 
+     * @param currentScan the info from the most recent pass
+     * @param oldScan info from the previous pass
+     */
+    public synchronized void reportDifferences (Map<String,TimeNSize> currentScan, Map<String,TimeNSize> oldScan) 
+    {
+        // scan the differences and add what was found to the map of notifications:
+
+        Set<String> oldScanKeys = new HashSet<String>(oldScan.keySet());
+        
+        // Look for new and changed files
+        for (Map.Entry<String, TimeNSize> entry: currentScan.entrySet())
+        {
+            String file = entry.getKey(); 
+            if (!oldScanKeys.contains(file))
+            {
+                Notification old=_notifications.put(file,Notification.ADDED);
+                if (old!=null)
+                { 
+                    switch(old)
+                    {
+                        case REMOVED: 
+                        case CHANGED:
+                            _notifications.put(file,Notification.CHANGED);
+                    }
+                }
+            }
+            else if (!oldScan.get(file).equals(currentScan.get(file)))
+            {
+                Notification old=_notifications.put(file,Notification.CHANGED);
+                if (old!=null)
+                {
+                    switch(old)
+                    {
+                        case ADDED:
+                            _notifications.put(file,Notification.ADDED);
+                    }
+                }
+            }
+        }
+        
+        // Look for deleted files
+        for (String file : oldScan.keySet())
+        {
+            if (!currentScan.containsKey(file))
+            {
+                Notification old=_notifications.put(file,Notification.REMOVED);
+                if (old!=null)
+                {
+                    switch(old)
+                    {
+                        case ADDED:
+                            _notifications.remove(file);
+                    }
+                }
+            }
+        }
+        
+        if (LOG.isDebugEnabled())
+            LOG.debug("scanned "+_scanDirs+": "+_notifications);
+                
+        // Process notifications
+        // Only process notifications that are for stable files (ie same in old and current scan).
+        List<String> bulkChanges = new ArrayList<String>();
+        for (Iterator<Entry<String,Notification>> iter = _notifications.entrySet().iterator();iter.hasNext();)
+        {
+            Entry<String,Notification> entry=iter.next();
+            String file=entry.getKey();
+            
+            // Is the file stable?
+            if (oldScan.containsKey(file))
+            {
+                if (!oldScan.get(file).equals(currentScan.get(file)))
+                    continue;
+            }
+            else if (currentScan.containsKey(file))
+                continue;
+                            
+            // File is stable so notify
+            Notification notification=entry.getValue();
+            iter.remove();
+            bulkChanges.add(file);
+            switch(notification)
+            {
+                case ADDED:
+                    reportAddition(file);
+                    break;
+                case CHANGED:
+                    reportChange(file);
+                    break;
+                case REMOVED:
+                    reportRemoval(file);
+                    break;
+            }
+        }
+        if (!bulkChanges.isEmpty())
+            reportBulkChanges(bulkChanges);
+    }
+
+
+    /**
+     * Get last modified time on a single file or recurse if
+     * the file is a directory. 
+     * @param f file or directory
+     * @param scanInfoMap map of filenames to last modified times
+     */
+    private void scanFile (File f, Map<String,TimeNSize> scanInfoMap, int depth)
+    {
+        try
+        {
+            if (!f.exists())
+                return;
+
+            if (f.isFile() || depth>0&& _reportDirs && f.isDirectory())
+            {
+                if ((_filter == null) || ((_filter != null) && _filter.accept(f.getParentFile(), f.getName())))
+                {
+                    String name = f.getCanonicalPath();
+                    scanInfoMap.put(name, new TimeNSize(f.lastModified(),f.length()));
+                }
+            }
+            
+            // If it is a directory, scan if it is a known directory or the depth is OK.
+            if (f.isDirectory() && (depth<_scanDepth || _scanDepth==-1 || _scanDirs.contains(f)))
+            {
+                File[] files = f.listFiles();
+                if (files != null)
+                {
+                    for (int i=0;i<files.length;i++)
+                        scanFile(files[i], scanInfoMap,depth+1);
+                }
+                else
+                    LOG.warn("Error listing files in directory {}", f);
+                    
+            }
+        }
+        catch (IOException e)
+        {
+            LOG.warn("Error scanning watched files", e);
+        }
+    }
+
+    private void warn(Object listener,String filename,Throwable th)
+    {
+        LOG.warn(listener+" failed on '"+filename, th);
+    }
+
+    /**
+     * Report a file addition to the registered FileAddedListeners
+     * @param filename
+     */
+    private void reportAddition (String filename)
+    {
+        Iterator<Listener> itor = _listeners.iterator();
+        while (itor.hasNext())
+        {
+            Listener l = itor.next();
+            try
+            {
+                if (l instanceof DiscreteListener)
+                    ((DiscreteListener)l).fileAdded(filename);
+            }
+            catch (Exception e)
+            {
+                warn(l,filename,e);
+            }
+            catch (Error e)
+            {
+                warn(l,filename,e);
+            }
+        }
+    }
+
+
+    /**
+     * Report a file removal to the FileRemovedListeners
+     * @param filename
+     */
+    private void reportRemoval (String filename)
+    {
+        Iterator<Listener> itor = _listeners.iterator();
+        while (itor.hasNext())
+        {
+            Object l = itor.next();
+            try
+            {
+                if (l instanceof DiscreteListener)
+                    ((DiscreteListener)l).fileRemoved(filename);
+            }
+            catch (Exception e)
+            {
+                warn(l,filename,e);
+            }
+            catch (Error e)
+            {
+                warn(l,filename,e);
+            }
+        }
+    }
+
+
+    /**
+     * Report a file change to the FileChangedListeners
+     * @param filename
+     */
+    private void reportChange (String filename)
+    {
+        Iterator<Listener> itor = _listeners.iterator();
+        while (itor.hasNext())
+        {
+            Listener l = itor.next();
+            try
+            {
+                if (l instanceof DiscreteListener)
+                    ((DiscreteListener)l).fileChanged(filename);
+            }
+            catch (Exception e)
+            {
+                warn(l,filename,e);
+            }
+            catch (Error e)
+            {
+                warn(l,filename,e);
+            }
+        }
+    }
+    
+    private void reportBulkChanges (List<String> filenames)
+    {
+        Iterator<Listener> itor = _listeners.iterator();
+        while (itor.hasNext())
+        {
+            Listener l = itor.next();
+            try
+            {
+                if (l instanceof BulkListener)
+                    ((BulkListener)l).filesChanged(filenames);
+            }
+            catch (Exception e)
+            {
+                warn(l,filenames.toString(),e);
+            }
+            catch (Error e)
+            {
+                warn(l,filenames.toString(),e);
+            }
+        }
+    }
+    
+    /**
+     * signal any scan cycle listeners that a scan has started
+     */
+    private void reportScanStart(int cycle)
+    {
+        for (Listener listener : _listeners)
+        {
+            try
+            {
+                if (listener instanceof ScanCycleListener)
+                {
+                    ((ScanCycleListener)listener).scanStarted(cycle);
+                }
+            }
+            catch (Exception e)
+            {
+                LOG.warn(listener + " failed on scan start for cycle " + cycle, e);
+            }
+        }
+    }
+
+    /**
+     * sign
+     */
+    private void reportScanEnd(int cycle)
+    {
+        for (Listener listener : _listeners)
+        {
+            try
+            {
+                if (listener instanceof ScanCycleListener)
+                {
+                    ((ScanCycleListener)listener).scanEnded(cycle);
+                }
+            }
+            catch (Exception e)
+            {
+                LOG.warn(listener + " failed on scan end for cycle " + cycle, e);
+            }
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/StringMap.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,695 @@
+//
+//  ========================================================================
+//  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.util;
+
+import java.io.Externalizable;
+import java.util.AbstractMap;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/* ------------------------------------------------------------ */
+/** Map implementation Optimized for Strings keys..
+ * This String Map has been optimized for mapping small sets of
+ * Strings where the most frequently accessed Strings have been put to
+ * the map first.
+ *
+ * It also has the benefit that it can look up entries by substring or
+ * sections of char and byte arrays.  This can prevent many String
+ * objects from being created just to look up in the map.
+ *
+ * This map is NOT synchronized.
+ */
+public class StringMap extends AbstractMap implements Externalizable
+{
+    public static final boolean CASE_INSENSTIVE=true;
+    protected static final int __HASH_WIDTH=17;
+    
+    /* ------------------------------------------------------------ */
+    protected int _width=__HASH_WIDTH;
+    protected Node _root=new Node();
+    protected boolean _ignoreCase=false;
+    protected NullEntry _nullEntry=null;
+    protected Object _nullValue=null;
+    protected HashSet _entrySet=new HashSet(3);
+    protected Set _umEntrySet=Collections.unmodifiableSet(_entrySet);
+    
+    /* ------------------------------------------------------------ */
+    /** Constructor. 
+     */
+    public StringMap()
+    {}
+    
+    /* ------------------------------------------------------------ */
+    /** Constructor. 
+     * @param ignoreCase 
+     */
+    public StringMap(boolean ignoreCase)
+    {
+        this();
+        _ignoreCase=ignoreCase;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Constructor. 
+     * @param ignoreCase 
+     * @param width Width of hash tables, larger values are faster but
+     * use more memory.
+     */
+    public StringMap(boolean ignoreCase,int width)
+    {
+        this();
+        _ignoreCase=ignoreCase;
+        _width=width;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Set the ignoreCase attribute.
+     * @param ic If true, the map is case insensitive for keys.
+     */
+    public void setIgnoreCase(boolean ic)
+    {
+        if (_root._children!=null)
+            throw new IllegalStateException("Must be set before first put");
+        _ignoreCase=ic;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isIgnoreCase()
+    {
+        return _ignoreCase;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set the hash width.
+     * @param width Width of hash tables, larger values are faster but
+     * use more memory.
+     */
+    public void setWidth(int width)
+    {
+        _width=width;
+    }
+
+    /* ------------------------------------------------------------ */
+    public int getWidth()
+    {
+        return _width;
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public Object put(Object key, Object value)
+    {
+        if (key==null)
+            return put(null,value);
+        return put(key.toString(),value);
+    }
+        
+    /* ------------------------------------------------------------ */
+    public Object put(String key, Object value)
+    {
+        if (key==null)
+        {
+            Object oldValue=_nullValue;
+            _nullValue=value;
+            if (_nullEntry==null)
+            {   
+                _nullEntry=new NullEntry();
+                _entrySet.add(_nullEntry);
+            }
+            return oldValue;
+        }
+        
+        Node node = _root;
+        int ni=-1;
+        Node prev = null;
+        Node parent = null;
+
+        // look for best match
+    charLoop:
+        for (int i=0;i<key.length();i++)
+        {
+            char c=key.charAt(i);
+            
+            // Advance node
+            if (ni==-1)
+            {
+                parent=node;
+                prev=null;
+                ni=0;
+                node=(node._children==null)?null:node._children[c%_width];
+            }
+            
+            // Loop through a node chain at the same level
+            while (node!=null) 
+            {
+                // If it is a matching node, goto next char
+                if (node._char[ni]==c || _ignoreCase&&node._ochar[ni]==c)
+                {
+                    prev=null;
+                    ni++;
+                    if (ni==node._char.length)
+                        ni=-1;
+                    continue charLoop;
+                }
+
+                // no char match
+                // if the first char,
+                if (ni==0)
+                {
+                    // look along the chain for a char match
+                    prev=node;
+                    node=node._next;
+                }
+                else
+                {
+                    // Split the current node!
+                    node.split(this,ni);
+                    i--;
+                    ni=-1;
+                    continue charLoop;
+                }
+            }
+
+            // We have run out of nodes, so as this is a put, make one
+            node = new Node(_ignoreCase,key,i);
+
+            if (prev!=null) // add to end of chain
+                prev._next=node;
+            else if (parent!=null) // add new child
+            {
+                if (parent._children==null)
+                    parent._children=new Node[_width];
+                parent._children[c%_width]=node;
+                int oi=node._ochar[0]%_width;
+                if (node._ochar!=null && node._char[0]%_width!=oi)
+                {
+                    if (parent._children[oi]==null)
+                        parent._children[oi]=node;
+                    else
+                    {
+                        Node n=parent._children[oi];
+                        while(n._next!=null)
+                            n=n._next;
+                        n._next=node;
+                    }
+                }
+            }
+            else // this is the root.
+                _root=node;
+            break;
+        }
+        
+        // Do we have a node
+        if (node!=null)
+        {
+            // Split it if we are in the middle
+            if(ni>0)
+                node.split(this,ni);
+        
+            Object old = node._value;
+            node._key=key;
+            node._value=value;
+            _entrySet.add(node);
+            return old;
+        }
+        return null;
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public Object get(Object key)
+    {
+        if (key==null)
+            return _nullValue;
+        if (key instanceof String)
+            return get((String)key);
+        return get(key.toString());
+    }
+    
+    /* ------------------------------------------------------------ */
+    public Object get(String key)
+    {
+        if (key==null)
+            return _nullValue;
+        
+        Map.Entry entry = getEntry(key,0,key.length());
+        if (entry==null)
+            return null;
+        return entry.getValue();
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Get a map entry by substring key.
+     * @param key String containing the key
+     * @param offset Offset of the key within the String.
+     * @param length The length of the key 
+     * @return The Map.Entry for the key or null if the key is not in
+     * the map.
+     */
+    public Map.Entry getEntry(String key,int offset, int length)
+    {
+        if (key==null)
+            return _nullEntry;
+        
+        Node node = _root;
+        int ni=-1;
+
+        // look for best match
+    charLoop:
+        for (int i=0;i<length;i++)
+        {
+            char c=key.charAt(offset+i);
+
+            // Advance node
+            if (ni==-1)
+            {
+                ni=0;
+                node=(node._children==null)?null:node._children[c%_width];
+            }
+            
+            // Look through the node chain
+            while (node!=null) 
+            {
+                // If it is a matching node, goto next char
+                if (node._char[ni]==c || _ignoreCase&&node._ochar[ni]==c)
+                {
+                    ni++;
+                    if (ni==node._char.length)
+                        ni=-1;
+                    continue charLoop;
+                }
+
+                // No char match, so if mid node then no match at all.
+                if (ni>0) return null;
+
+                // try next in chain
+                node=node._next;                
+            }
+            return null;
+        }
+        
+        if (ni>0) return null;
+        if (node!=null && node._key==null)
+            return null;
+        return node;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Get a map entry by char array key.
+     * @param key char array containing the key
+     * @param offset Offset of the key within the array.
+     * @param length The length of the key 
+     * @return The Map.Entry for the key or null if the key is not in
+     * the map.
+     */
+    public Map.Entry getEntry(char[] key,int offset, int length)
+    {
+        if (key==null)
+            return _nullEntry;
+        
+        Node node = _root;
+        int ni=-1;
+
+        // look for best match
+    charLoop:
+        for (int i=0;i<length;i++)
+        {
+            char c=key[offset+i];
+
+            // Advance node
+            if (ni==-1)
+            {
+                ni=0;
+                node=(node._children==null)?null:node._children[c%_width];
+            }
+            
+            // While we have a node to try
+            while (node!=null) 
+            {
+                // If it is a matching node, goto next char
+                if (node._char[ni]==c || _ignoreCase&&node._ochar[ni]==c)
+                {
+                    ni++;
+                    if (ni==node._char.length)
+                        ni=-1;
+                    continue charLoop;
+                }
+
+                // No char match, so if mid node then no match at all.
+                if (ni>0) return null;
+
+                // try next in chain
+                node=node._next;                
+            }
+            return null;
+        }
+        
+        if (ni>0) return null;
+        if (node!=null && node._key==null)
+            return null;
+        return node;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Get a map entry by byte array key, using as much of the passed key as needed for a match.
+     * A simple 8859-1 byte to char mapping is assumed.
+     * @param key char array containing the key
+     * @param offset Offset of the key within the array.
+     * @param maxLength The length of the key 
+     * @return The Map.Entry for the key or null if the key is not in
+     * the map.
+     */
+    public Map.Entry getBestEntry(byte[] key,int offset, int maxLength)
+    {
+        if (key==null)
+            return _nullEntry;
+        
+        Node node = _root;
+        int ni=-1;
+
+        // look for best match
+    charLoop:
+        for (int i=0;i<maxLength;i++)
+        {
+            char c=(char)key[offset+i];
+
+            // Advance node
+            if (ni==-1)
+            {
+                ni=0;
+                
+                Node child = (node._children==null)?null:node._children[c%_width];
+                
+                if (child==null && i>0)
+                    return node; // This is the best match
+                node=child;           
+            }
+            
+            // While we have a node to try
+            while (node!=null) 
+            {
+                // If it is a matching node, goto next char
+                if (node._char[ni]==c || _ignoreCase&&node._ochar[ni]==c)
+                {
+                    ni++;
+                    if (ni==node._char.length)
+                        ni=-1;
+                    continue charLoop;
+                }
+
+                // No char match, so if mid node then no match at all.
+                if (ni>0) return null;
+
+                // try next in chain
+                node=node._next;                
+            }
+            return null;
+        }
+        
+        if (ni>0) return null;
+        if (node!=null && node._key==null)
+            return null;
+        return node;
+    }
+    
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public Object remove(Object key)
+    {
+        if (key==null)
+            return remove(null);
+        return remove(key.toString());
+    }
+    
+    /* ------------------------------------------------------------ */
+    public Object remove(String key)
+    {
+        if (key==null)
+        {
+            Object oldValue=_nullValue;
+            if (_nullEntry!=null)
+            {
+                _entrySet.remove(_nullEntry);   
+                _nullEntry=null;
+                _nullValue=null;
+            }
+            return oldValue;
+        }
+        
+        Node node = _root;
+        int ni=-1;
+
+        // look for best match
+    charLoop:
+        for (int i=0;i<key.length();i++)
+        {
+            char c=key.charAt(i);
+
+            // Advance node
+            if (ni==-1)
+            {
+                ni=0;
+                node=(node._children==null)?null:node._children[c%_width];
+            }
+            
+            // While we have a node to try
+            while (node!=null) 
+            {
+                // If it is a matching node, goto next char
+                if (node._char[ni]==c || _ignoreCase&&node._ochar[ni]==c)
+                {
+                    ni++;
+                    if (ni==node._char.length)
+                        ni=-1;
+                    continue charLoop;
+                }
+
+                // No char match, so if mid node then no match at all.
+                if (ni>0) return null;
+
+                // try next in chain
+                node=node._next;         
+            }
+            return null;
+        }
+
+        if (ni>0) return null;
+        if (node!=null && node._key==null)
+            return null;
+        
+        Object old = node._value;
+        _entrySet.remove(node);
+        node._value=null;
+        node._key=null;
+        
+        return old; 
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public Set entrySet()
+    {
+        return _umEntrySet;
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public int size()
+    {
+        return _entrySet.size();
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public boolean isEmpty()
+    {
+        return _entrySet.isEmpty();
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public boolean containsKey(Object key)
+    {
+        if (key==null)
+            return _nullEntry!=null;
+        return
+            getEntry(key.toString(),0,key==null?0:key.toString().length())!=null;
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public void clear()
+    {
+        _root=new Node();
+        _nullEntry=null;
+        _nullValue=null;
+        _entrySet.clear();
+    }
+
+    
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    private static class Node implements Map.Entry
+    {
+        char[] _char;
+        char[] _ochar;
+        Node _next;
+        Node[] _children;
+        String _key;
+        Object _value;
+        
+        Node(){}
+        
+        Node(boolean ignoreCase,String s, int offset)
+        {
+            int l=s.length()-offset;
+            _char=new char[l];
+            _ochar=new char[l];
+            for (int i=0;i<l;i++)
+            {
+                char c=s.charAt(offset+i);
+                _char[i]=c;
+                if (ignoreCase)
+                {
+                    char o=c;
+                    if (Character.isUpperCase(c))
+                        o=Character.toLowerCase(c);
+                    else if (Character.isLowerCase(c))
+                        o=Character.toUpperCase(c);
+                    _ochar[i]=o;
+                }
+            }
+        }
+
+        Node split(StringMap map,int offset)
+        {
+            Node split = new Node();
+            int sl=_char.length-offset;
+            
+            char[] tmp=this._char;
+            this._char=new char[offset];
+            split._char = new char[sl];
+            System.arraycopy(tmp,0,this._char,0,offset);
+            System.arraycopy(tmp,offset,split._char,0,sl);
+
+            if (this._ochar!=null)
+            {
+                tmp=this._ochar;
+                this._ochar=new char[offset];
+                split._ochar = new char[sl];
+                System.arraycopy(tmp,0,this._ochar,0,offset);
+                System.arraycopy(tmp,offset,split._ochar,0,sl);
+            }
+            
+            split._key=this._key;
+            split._value=this._value;
+            this._key=null;
+            this._value=null;
+            if (map._entrySet.remove(this))
+                map._entrySet.add(split);
+
+            split._children=this._children;            
+            this._children=new Node[map._width];
+            this._children[split._char[0]%map._width]=split;
+            if (split._ochar!=null && this._children[split._ochar[0]%map._width]!=split)
+                this._children[split._ochar[0]%map._width]=split;
+
+            return split;
+        }
+        
+        public Object getKey(){return _key;}
+        public Object getValue(){return _value;}
+        public Object setValue(Object o){Object old=_value;_value=o;return old;}
+        @Override
+        public String toString()
+        {
+            StringBuilder buf=new StringBuilder();
+            toString(buf);
+            return buf.toString();
+        }
+
+        private void toString(StringBuilder buf)
+        {
+            buf.append("{[");
+            if (_char==null)
+                buf.append('-');
+            else
+                for (int i=0;i<_char.length;i++)
+                    buf.append(_char[i]);
+            buf.append(':');
+            buf.append(_key);
+            buf.append('=');
+            buf.append(_value);
+            buf.append(']');
+            if (_children!=null)
+            {
+                for (int i=0;i<_children.length;i++)
+                {
+                    buf.append('|');
+                    if (_children[i]!=null)
+                        _children[i].toString(buf);
+                    else
+                        buf.append("-");
+                }
+            }
+            buf.append('}');
+            if (_next!=null)
+            {
+                buf.append(",\n");
+                _next.toString(buf);
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    private class NullEntry implements Map.Entry
+    {
+        public Object getKey(){return null;}
+        public Object getValue(){return _nullValue;}
+        public Object setValue(Object o)
+            {Object old=_nullValue;_nullValue=o;return old;}
+        @Override
+        public String toString(){return "[:null="+_nullValue+"]";}
+    }
+
+    /* ------------------------------------------------------------ */
+    public void writeExternal(java.io.ObjectOutput out)
+        throws java.io.IOException
+    {
+        HashMap map = new HashMap(this);
+        out.writeBoolean(_ignoreCase);
+        out.writeObject(map);
+    }
+    
+    /* ------------------------------------------------------------ */
+    public void readExternal(java.io.ObjectInput in)
+        throws java.io.IOException, ClassNotFoundException
+    {
+        boolean ic=in.readBoolean();
+        HashMap map = (HashMap)in.readObject();
+        setIgnoreCase(ic);
+        this.putAll(map);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/StringUtil.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,504 @@
+//
+//  ========================================================================
+//  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.util;
+
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/** Fast String Utilities.
+ *
+ * These string utilities provide both conveniance methods and
+ * performance improvements over most standard library versions. The
+ * main aim of the optimizations is to avoid object creation unless
+ * absolutely required.
+ *
+ * 
+ */
+public class StringUtil
+{
+    private static final Logger LOG = Log.getLogger(StringUtil.class);
+    
+    public static final String ALL_INTERFACES="0.0.0.0";
+    public static final String CRLF="\015\012";
+    public static final String __LINE_SEPARATOR=
+        System.getProperty("line.separator","\n");
+       
+    public static final String __ISO_8859_1="ISO-8859-1";
+    public final static String __UTF8="UTF-8";
+    public final static String __UTF8Alt="UTF8";
+    public final static String __UTF16="UTF-16";
+    
+    public final static Charset __UTF8_CHARSET;
+    public final static Charset __ISO_8859_1_CHARSET;
+    
+    static
+    {
+        __UTF8_CHARSET=Charset.forName(__UTF8);
+        __ISO_8859_1_CHARSET=Charset.forName(__ISO_8859_1);
+    }
+    
+    private static char[] lowercases = {
+          '\000','\001','\002','\003','\004','\005','\006','\007',
+          '\010','\011','\012','\013','\014','\015','\016','\017',
+          '\020','\021','\022','\023','\024','\025','\026','\027',
+          '\030','\031','\032','\033','\034','\035','\036','\037',
+          '\040','\041','\042','\043','\044','\045','\046','\047',
+          '\050','\051','\052','\053','\054','\055','\056','\057',
+          '\060','\061','\062','\063','\064','\065','\066','\067',
+          '\070','\071','\072','\073','\074','\075','\076','\077',
+          '\100','\141','\142','\143','\144','\145','\146','\147',
+          '\150','\151','\152','\153','\154','\155','\156','\157',
+          '\160','\161','\162','\163','\164','\165','\166','\167',
+          '\170','\171','\172','\133','\134','\135','\136','\137',
+          '\140','\141','\142','\143','\144','\145','\146','\147',
+          '\150','\151','\152','\153','\154','\155','\156','\157',
+          '\160','\161','\162','\163','\164','\165','\166','\167',
+          '\170','\171','\172','\173','\174','\175','\176','\177' };
+
+    /* ------------------------------------------------------------ */
+    /**
+     * fast lower case conversion. Only works on ascii (not unicode)
+     * @param s the string to convert
+     * @return a lower case version of s
+     */
+    public static String asciiToLowerCase(String s)
+    {
+        char[] c = null;
+        int i=s.length();
+
+        // look for first conversion
+        while (i-->0)
+        {
+            char c1=s.charAt(i);
+            if (c1<=127)
+            {
+                char c2=lowercases[c1];
+                if (c1!=c2)
+                {
+                    c=s.toCharArray();
+                    c[i]=c2;
+                    break;
+                }
+            }
+        }
+
+        while (i-->0)
+        {
+            if(c[i]<=127)
+                c[i] = lowercases[c[i]];
+        }
+        
+        return c==null?s:new String(c);
+    }
+
+
+    /* ------------------------------------------------------------ */
+    public static boolean startsWithIgnoreCase(String s,String w)
+    {
+        if (w==null)
+            return true;
+        
+        if (s==null || s.length()<w.length())
+            return false;
+        
+        for (int i=0;i<w.length();i++)
+        {
+            char c1=s.charAt(i);
+            char c2=w.charAt(i);
+            if (c1!=c2)
+            {
+                if (c1<=127)
+                    c1=lowercases[c1];
+                if (c2<=127)
+                    c2=lowercases[c2];
+                if (c1!=c2)
+                    return false;
+            }
+        }
+        return true;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public static boolean endsWithIgnoreCase(String s,String w)
+    {
+        if (w==null)
+            return true;
+
+        if (s==null)
+            return false;
+            
+        int sl=s.length();
+        int wl=w.length();
+        
+        if (sl<wl)
+            return false;
+        
+        for (int i=wl;i-->0;)
+        {
+            char c1=s.charAt(--sl);
+            char c2=w.charAt(i);
+            if (c1!=c2)
+            {
+                if (c1<=127)
+                    c1=lowercases[c1];
+                if (c2<=127)
+                    c2=lowercases[c2];
+                if (c1!=c2)
+                    return false;
+            }
+        }
+        return true;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * returns the next index of a character from the chars string
+     */
+    public static int indexFrom(String s,String chars)
+    {
+        for (int i=0;i<s.length();i++)
+           if (chars.indexOf(s.charAt(i))>=0)
+              return i;
+        return -1;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * replace substrings within string.
+     */
+    public static String replace(String s, String sub, String with)
+    {
+        int c=0;
+        int i=s.indexOf(sub,c);
+        if (i == -1)
+            return s;
+    
+        StringBuilder buf = new StringBuilder(s.length()+with.length());
+
+        do
+        {
+            buf.append(s.substring(c,i));
+            buf.append(with);
+            c=i+sub.length();
+        } while ((i=s.indexOf(sub,c))!=-1);
+
+        if (c<s.length())
+            buf.append(s.substring(c,s.length()));
+
+        return buf.toString();
+        
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /** Remove single or double quotes.
+     */
+    public static String unquote(String s)
+    {
+        return QuotedStringTokenizer.unquote(s);
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /** Append substring to StringBuilder 
+     * @param buf StringBuilder to append to
+     * @param s String to append from
+     * @param offset The offset of the substring
+     * @param length The length of the substring
+     */
+    public static void append(StringBuilder buf,
+                              String s,
+                              int offset,
+                              int length)
+    {
+        synchronized(buf)
+        {
+            int end=offset+length;
+            for (int i=offset; i<end;i++)
+            {
+                if (i>=s.length())
+                    break;
+                buf.append(s.charAt(i));
+            }
+        }
+    }
+
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * append hex digit
+     * 
+     */
+    public static void append(StringBuilder buf,byte b,int base)
+    {
+        int bi=0xff&b;
+        int c='0'+(bi/base)%base;
+        if (c>'9')
+            c= 'a'+(c-'0'-10);
+        buf.append((char)c);
+        c='0'+bi%base;
+        if (c>'9')
+            c= 'a'+(c-'0'-10);
+        buf.append((char)c);
+    }
+
+    /* ------------------------------------------------------------ */
+    public static void append2digits(StringBuffer buf,int i)
+    {
+        if (i<100)
+        {
+            buf.append((char)(i/10+'0'));
+            buf.append((char)(i%10+'0'));
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    public static void append2digits(StringBuilder buf,int i)
+    {
+        if (i<100)
+        {
+            buf.append((char)(i/10+'0'));
+            buf.append((char)(i%10+'0'));
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Return a non null string.
+     * @param s String
+     * @return The string passed in or empty string if it is null. 
+     */
+    public static String nonNull(String s)
+    {
+        if (s==null)
+            return "";
+        return s;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public static boolean equals(String s,char[] buf, int offset, int length)
+    {
+        if (s.length()!=length)
+            return false;
+        for (int i=0;i<length;i++)
+            if (buf[offset+i]!=s.charAt(i))
+                return false;
+        return true;
+    }
+
+    /* ------------------------------------------------------------ */
+    public static String toUTF8String(byte[] b,int offset,int length)
+    {
+        try
+        {
+            return new String(b,offset,length,__UTF8);
+        }
+        catch (UnsupportedEncodingException e)
+        {
+            throw new IllegalArgumentException(e);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public static String toString(byte[] b,int offset,int length,String charset)
+    {
+        try
+        {
+            return new String(b,offset,length,charset);
+        }
+        catch (UnsupportedEncodingException e)
+        {
+            throw new IllegalArgumentException(e);
+        }
+    }
+
+
+    /* ------------------------------------------------------------ */
+    public static boolean isUTF8(String charset)
+    {
+        return __UTF8.equalsIgnoreCase(charset)||__UTF8Alt.equalsIgnoreCase(charset);
+    }
+
+
+    /* ------------------------------------------------------------ */
+    public static String printable(String name)
+    {
+        if (name==null)
+            return null;
+        StringBuilder buf = new StringBuilder(name.length());
+        for (int i=0;i<name.length();i++)
+        {
+            char c=name.charAt(i);
+            if (!Character.isISOControl(c))
+                buf.append(c);
+        }
+        return buf.toString();
+    }
+    
+    /* ------------------------------------------------------------ */
+    public static String printable(byte[] b)
+    {
+        StringBuilder buf = new StringBuilder();
+        for (int i=0;i<b.length;i++)
+        {
+            char c=(char)b[i];
+            if (Character.isWhitespace(c)|| c>' ' && c<0x7f)
+                buf.append(c);
+            else 
+            {
+                buf.append("0x");
+                TypeUtil.toHex(b[i],buf);
+            }
+        }
+        return buf.toString();
+    }
+    
+    public static byte[] getBytes(String s)
+    {
+        try
+        {
+            return s.getBytes(__ISO_8859_1);
+        }
+        catch(Exception e)
+        {
+            LOG.warn(e);
+            return s.getBytes();
+        }
+    }
+    
+    public static byte[] getBytes(String s,String charset)
+    {
+        try
+        {
+            return s.getBytes(charset);
+        }
+        catch(Exception e)
+        {
+            LOG.warn(e);
+            return s.getBytes();
+        }
+    }
+    
+    
+    
+    /**
+     * Converts a binary SID to a string SID
+     * 
+     * http://en.wikipedia.org/wiki/Security_Identifier
+     * 
+     * S-1-IdentifierAuthority-SubAuthority1-SubAuthority2-...-SubAuthorityn
+     */
+    public static String sidBytesToString(byte[] sidBytes)
+    {
+        StringBuilder sidString = new StringBuilder();
+        
+        // Identify this as a SID
+        sidString.append("S-");
+        
+        // Add SID revision level (expect 1 but may change someday)
+        sidString.append(Byte.toString(sidBytes[0])).append('-');
+        
+        StringBuilder tmpBuilder = new StringBuilder();
+        
+        // crunch the six bytes of issuing authority value
+        for (int i = 2; i <= 7; ++i)
+        {
+            tmpBuilder.append(Integer.toHexString(sidBytes[i] & 0xFF));
+        }
+        
+        sidString.append(Long.parseLong(tmpBuilder.toString(), 16)); // '-' is in the subauth loop
+   
+        // the number of subAuthorities we need to attach
+        int subAuthorityCount = sidBytes[1];
+
+        // attach each of the subAuthorities
+        for (int i = 0; i < subAuthorityCount; ++i)
+        {
+            int offset = i * 4;
+            tmpBuilder.setLength(0);
+            // these need to be zero padded hex and little endian
+            tmpBuilder.append(String.format("%02X%02X%02X%02X", 
+                    (sidBytes[11 + offset] & 0xFF),
+                    (sidBytes[10 + offset] & 0xFF),
+                    (sidBytes[9 + offset] & 0xFF),
+                    (sidBytes[8 + offset] & 0xFF)));  
+            sidString.append('-').append(Long.parseLong(tmpBuilder.toString(), 16));
+        }
+        
+        return sidString.toString();
+    }
+    
+    /**
+     * Converts a string SID to a binary SID
+     * 
+     * http://en.wikipedia.org/wiki/Security_Identifier
+     * 
+     * S-1-IdentifierAuthority-SubAuthority1-SubAuthority2-...-SubAuthorityn
+     */
+    public static byte[] sidStringToBytes( String sidString )
+    {
+        String[] sidTokens = sidString.split("-");
+        
+        int subAuthorityCount = sidTokens.length - 3; // S-Rev-IdAuth-
+        
+        int byteCount = 0;
+        byte[] sidBytes = new byte[1 + 1 + 6 + (4 * subAuthorityCount)];
+        
+        // the revision byte
+        sidBytes[byteCount++] = (byte)Integer.parseInt(sidTokens[1]);
+
+        // the # of sub authorities byte
+        sidBytes[byteCount++] = (byte)subAuthorityCount;
+
+        // the certAuthority
+        String hexStr = Long.toHexString(Long.parseLong(sidTokens[2]));
+        
+        while( hexStr.length() < 12) // pad to 12 characters
+        {
+            hexStr = "0" + hexStr;
+        }
+
+        // place the certAuthority 6 bytes
+        for ( int i = 0 ; i < hexStr.length(); i = i + 2)
+        {
+            sidBytes[byteCount++] = (byte)Integer.parseInt(hexStr.substring(i, i + 2),16);
+        }
+                
+        
+        for ( int i = 3; i < sidTokens.length ; ++i)
+        {
+            hexStr = Long.toHexString(Long.parseLong(sidTokens[i]));
+            
+            while( hexStr.length() < 8) // pad to 8 characters
+            {
+                hexStr = "0" + hexStr;
+            }     
+            
+            // place the inverted sub authorities, 4 bytes each
+            for ( int j = hexStr.length(); j > 0; j = j - 2)
+            {          
+                sidBytes[byteCount++] = (byte)Integer.parseInt(hexStr.substring(j-2, j),16);
+            }
+        }
+      
+        return sidBytes;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/TypeUtil.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,593 @@
+//
+//  ========================================================================
+//  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.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+
+/* ------------------------------------------------------------ */
+/**
+ * TYPE Utilities.
+ * Provides various static utiltiy methods for manipulating types and their
+ * string representations.
+ *
+ * @since Jetty 4.1
+ */
+public class TypeUtil
+{
+    private static final Logger LOG = Log.getLogger(TypeUtil.class);
+    public static int CR = '\015';
+    public static int LF = '\012';
+
+    /* ------------------------------------------------------------ */
+    private static final HashMap<String, Class<?>> name2Class=new HashMap<String, Class<?>>();
+    static
+    {
+        name2Class.put("boolean",java.lang.Boolean.TYPE);
+        name2Class.put("byte",java.lang.Byte.TYPE);
+        name2Class.put("char",java.lang.Character.TYPE);
+        name2Class.put("double",java.lang.Double.TYPE);
+        name2Class.put("float",java.lang.Float.TYPE);
+        name2Class.put("int",java.lang.Integer.TYPE);
+        name2Class.put("long",java.lang.Long.TYPE);
+        name2Class.put("short",java.lang.Short.TYPE);
+        name2Class.put("void",java.lang.Void.TYPE);
+
+        name2Class.put("java.lang.Boolean.TYPE",java.lang.Boolean.TYPE);
+        name2Class.put("java.lang.Byte.TYPE",java.lang.Byte.TYPE);
+        name2Class.put("java.lang.Character.TYPE",java.lang.Character.TYPE);
+        name2Class.put("java.lang.Double.TYPE",java.lang.Double.TYPE);
+        name2Class.put("java.lang.Float.TYPE",java.lang.Float.TYPE);
+        name2Class.put("java.lang.Integer.TYPE",java.lang.Integer.TYPE);
+        name2Class.put("java.lang.Long.TYPE",java.lang.Long.TYPE);
+        name2Class.put("java.lang.Short.TYPE",java.lang.Short.TYPE);
+        name2Class.put("java.lang.Void.TYPE",java.lang.Void.TYPE);
+
+        name2Class.put("java.lang.Boolean",java.lang.Boolean.class);
+        name2Class.put("java.lang.Byte",java.lang.Byte.class);
+        name2Class.put("java.lang.Character",java.lang.Character.class);
+        name2Class.put("java.lang.Double",java.lang.Double.class);
+        name2Class.put("java.lang.Float",java.lang.Float.class);
+        name2Class.put("java.lang.Integer",java.lang.Integer.class);
+        name2Class.put("java.lang.Long",java.lang.Long.class);
+        name2Class.put("java.lang.Short",java.lang.Short.class);
+
+        name2Class.put("Boolean",java.lang.Boolean.class);
+        name2Class.put("Byte",java.lang.Byte.class);
+        name2Class.put("Character",java.lang.Character.class);
+        name2Class.put("Double",java.lang.Double.class);
+        name2Class.put("Float",java.lang.Float.class);
+        name2Class.put("Integer",java.lang.Integer.class);
+        name2Class.put("Long",java.lang.Long.class);
+        name2Class.put("Short",java.lang.Short.class);
+
+        name2Class.put(null,java.lang.Void.TYPE);
+        name2Class.put("string",java.lang.String.class);
+        name2Class.put("String",java.lang.String.class);
+        name2Class.put("java.lang.String",java.lang.String.class);
+    }
+
+    /* ------------------------------------------------------------ */
+    private static final HashMap<Class<?>, String> class2Name=new HashMap<Class<?>, String>();
+    static
+    {
+        class2Name.put(java.lang.Boolean.TYPE,"boolean");
+        class2Name.put(java.lang.Byte.TYPE,"byte");
+        class2Name.put(java.lang.Character.TYPE,"char");
+        class2Name.put(java.lang.Double.TYPE,"double");
+        class2Name.put(java.lang.Float.TYPE,"float");
+        class2Name.put(java.lang.Integer.TYPE,"int");
+        class2Name.put(java.lang.Long.TYPE,"long");
+        class2Name.put(java.lang.Short.TYPE,"short");
+        class2Name.put(java.lang.Void.TYPE,"void");
+
+        class2Name.put(java.lang.Boolean.class,"java.lang.Boolean");
+        class2Name.put(java.lang.Byte.class,"java.lang.Byte");
+        class2Name.put(java.lang.Character.class,"java.lang.Character");
+        class2Name.put(java.lang.Double.class,"java.lang.Double");
+        class2Name.put(java.lang.Float.class,"java.lang.Float");
+        class2Name.put(java.lang.Integer.class,"java.lang.Integer");
+        class2Name.put(java.lang.Long.class,"java.lang.Long");
+        class2Name.put(java.lang.Short.class,"java.lang.Short");
+
+        class2Name.put(null,"void");
+        class2Name.put(java.lang.String.class,"java.lang.String");
+    }
+
+    /* ------------------------------------------------------------ */
+    private static final HashMap<Class<?>, Method> class2Value=new HashMap<Class<?>, Method>();
+    static
+    {
+        try
+        {
+            Class<?>[] s ={java.lang.String.class};
+
+            class2Value.put(java.lang.Boolean.TYPE,
+                           java.lang.Boolean.class.getMethod("valueOf",s));
+            class2Value.put(java.lang.Byte.TYPE,
+                           java.lang.Byte.class.getMethod("valueOf",s));
+            class2Value.put(java.lang.Double.TYPE,
+                           java.lang.Double.class.getMethod("valueOf",s));
+            class2Value.put(java.lang.Float.TYPE,
+                           java.lang.Float.class.getMethod("valueOf",s));
+            class2Value.put(java.lang.Integer.TYPE,
+                           java.lang.Integer.class.getMethod("valueOf",s));
+            class2Value.put(java.lang.Long.TYPE,
+                           java.lang.Long.class.getMethod("valueOf",s));
+            class2Value.put(java.lang.Short.TYPE,
+                           java.lang.Short.class.getMethod("valueOf",s));
+
+            class2Value.put(java.lang.Boolean.class,
+                           java.lang.Boolean.class.getMethod("valueOf",s));
+            class2Value.put(java.lang.Byte.class,
+                           java.lang.Byte.class.getMethod("valueOf",s));
+            class2Value.put(java.lang.Double.class,
+                           java.lang.Double.class.getMethod("valueOf",s));
+            class2Value.put(java.lang.Float.class,
+                           java.lang.Float.class.getMethod("valueOf",s));
+            class2Value.put(java.lang.Integer.class,
+                           java.lang.Integer.class.getMethod("valueOf",s));
+            class2Value.put(java.lang.Long.class,
+                           java.lang.Long.class.getMethod("valueOf",s));
+            class2Value.put(java.lang.Short.class,
+                           java.lang.Short.class.getMethod("valueOf",s));
+        }
+        catch(Exception e)
+        {
+            throw new Error(e);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Array to List.
+     * <p>
+     * Works like {@link Arrays#asList(Object...)}, but handles null arrays.
+     * @return a list backed by the array.
+     */
+    public static <T> List<T> asList(T[] a) 
+    {
+        if (a==null)
+            return Collections.emptyList();
+        return Arrays.asList(a);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Class from a canonical name for a type.
+     * @param name A class or type name.
+     * @return A class , which may be a primitive TYPE field..
+     */
+    public static Class<?> fromName(String name)
+    {
+        return name2Class.get(name);
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Canonical name for a type.
+     * @param type A class , which may be a primitive TYPE field.
+     * @return Canonical name.
+     */
+    public static String toName(Class<?> type)
+    {
+        return class2Name.get(type);
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Convert String value to instance.
+     * @param type The class of the instance, which may be a primitive TYPE field.
+     * @param value The value as a string.
+     * @return The value as an Object.
+     */
+    public static Object valueOf(Class<?> type, String value)
+    {
+        try
+        {
+            if (type.equals(java.lang.String.class))
+                return value;
+
+            Method m = class2Value.get(type);
+            if (m!=null)
+                return m.invoke(null, value);
+
+            if (type.equals(java.lang.Character.TYPE) ||
+                type.equals(java.lang.Character.class))
+                return new Character(value.charAt(0));
+
+            Constructor<?> c = type.getConstructor(java.lang.String.class);
+            return c.newInstance(value);
+        }
+        catch(NoSuchMethodException e)
+        {
+            // LogSupport.ignore(log,e);
+        }
+        catch(IllegalAccessException e)
+        {
+            // LogSupport.ignore(log,e);
+        }
+        catch(InstantiationException e)
+        {
+            // LogSupport.ignore(log,e);
+        }
+        catch(InvocationTargetException e)
+        {
+            if (e.getTargetException() instanceof Error)
+                throw (Error)(e.getTargetException());
+            // LogSupport.ignore(log,e);
+        }
+        return null;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Convert String value to instance.
+     * @param type classname or type (eg int)
+     * @param value The value as a string.
+     * @return The value as an Object.
+     */
+    public static Object valueOf(String type, String value)
+    {
+        return valueOf(fromName(type),value);
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Parse an int from a substring.
+     * Negative numbers are not handled.
+     * @param s String
+     * @param offset Offset within string
+     * @param length Length of integer or -1 for remainder of string
+     * @param base base of the integer
+     * @return the parsed integer
+     * @throws NumberFormatException if the string cannot be parsed
+     */
+    public static int parseInt(String s, int offset, int length, int base)
+        throws NumberFormatException
+    {
+        int value=0;
+
+        if (length<0)
+            length=s.length()-offset;
+
+        for (int i=0;i<length;i++)
+        {
+            char c=s.charAt(offset+i);
+
+            int digit=convertHexDigit((int)c);
+            if (digit<0 || digit>=base)
+                throw new NumberFormatException(s.substring(offset,offset+length));
+            value=value*base+digit;
+        }
+        return value;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Parse an int from a byte array of ascii characters.
+     * Negative numbers are not handled.
+     * @param b byte array
+     * @param offset Offset within string
+     * @param length Length of integer or -1 for remainder of string
+     * @param base base of the integer
+     * @return the parsed integer
+     * @throws NumberFormatException if the array cannot be parsed into an integer
+     */
+    public static int parseInt(byte[] b, int offset, int length, int base)
+        throws NumberFormatException
+    {
+        int value=0;
+
+        if (length<0)
+            length=b.length-offset;
+
+        for (int i=0;i<length;i++)
+        {
+            char c=(char)(0xff&b[offset+i]);
+
+            int digit=c-'0';
+            if (digit<0 || digit>=base || digit>=10)
+            {
+                digit=10+c-'A';
+                if (digit<10 || digit>=base)
+                    digit=10+c-'a';
+            }
+            if (digit<0 || digit>=base)
+                throw new NumberFormatException(new String(b,offset,length));
+            value=value*base+digit;
+        }
+        return value;
+    }
+
+    /* ------------------------------------------------------------ */
+    public static byte[] parseBytes(String s, int base)
+    {
+        byte[] bytes=new byte[s.length()/2];
+        for (int i=0;i<s.length();i+=2)
+            bytes[i/2]=(byte)TypeUtil.parseInt(s,i,2,base);
+        return bytes;
+    }
+
+    /* ------------------------------------------------------------ */
+    public static String toString(byte[] bytes, int base)
+    {
+        StringBuilder buf = new StringBuilder();
+        for (byte b : bytes)
+        {
+            int bi=0xff&b;
+            int c='0'+(bi/base)%base;
+            if (c>'9')
+                c= 'a'+(c-'0'-10);
+            buf.append((char)c);
+            c='0'+bi%base;
+            if (c>'9')
+                c= 'a'+(c-'0'-10);
+            buf.append((char)c);
+        }
+        return buf.toString();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param c An ASCII encoded character 0-9 a-f A-F
+     * @return The byte value of the character 0-16.
+     */
+    public static byte convertHexDigit( byte c )
+    {
+        byte b = (byte)((c & 0x1f) + ((c >> 6) * 0x19) - 0x10);
+        if (b<0 || b>15)
+            throw new IllegalArgumentException("!hex "+c);
+        return b;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @param c An ASCII encoded character 0-9 a-f A-F
+     * @return The byte value of the character 0-16.
+     */
+    public static int convertHexDigit( int c )
+    {
+        int d= ((c & 0x1f) + ((c >> 6) * 0x19) - 0x10);
+        if (d<0 || d>15)
+            throw new NumberFormatException("!hex "+c);
+        return d;
+    }
+
+    /* ------------------------------------------------------------ */
+    public static void toHex(byte b,Appendable buf)
+    {
+        try
+        {
+            int d=0xf&((0xF0&b)>>4);
+            buf.append((char)((d>9?('A'-10):'0')+d));
+            d=0xf&b;
+            buf.append((char)((d>9?('A'-10):'0')+d));
+        }
+        catch(IOException e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public static void toHex(int value,Appendable buf) throws IOException
+    {
+        int d=0xf&((0xF0000000&value)>>28);
+        buf.append((char)((d>9?('A'-10):'0')+d));
+        d=0xf&((0x0F000000&value)>>24);
+        buf.append((char)((d>9?('A'-10):'0')+d));
+        d=0xf&((0x00F00000&value)>>20);
+        buf.append((char)((d>9?('A'-10):'0')+d));
+        d=0xf&((0x000F0000&value)>>16);
+        buf.append((char)((d>9?('A'-10):'0')+d));
+        d=0xf&((0x0000F000&value)>>12);
+        buf.append((char)((d>9?('A'-10):'0')+d));
+        d=0xf&((0x00000F00&value)>>8);
+        buf.append((char)((d>9?('A'-10):'0')+d));
+        d=0xf&((0x000000F0&value)>>4);
+        buf.append((char)((d>9?('A'-10):'0')+d));
+        d=0xf&value;
+        buf.append((char)((d>9?('A'-10):'0')+d));
+    
+        Integer.toString(0,36);
+    }
+    
+    
+    /* ------------------------------------------------------------ */
+    public static void toHex(long value,Appendable buf) throws IOException
+    {
+        toHex((int)(value>>32),buf);
+        toHex((int)value,buf);
+    }
+
+    /* ------------------------------------------------------------ */
+    public static String toHexString(byte b)
+    {
+        return toHexString(new byte[]{b}, 0, 1);
+    }
+    
+    /* ------------------------------------------------------------ */
+    public static String toHexString(byte[] b)
+    {
+        return toHexString(b, 0, b.length);
+    }
+
+    /* ------------------------------------------------------------ */
+    public static String toHexString(byte[] b,int offset,int length)
+    {
+        StringBuilder buf = new StringBuilder();
+        for (int i=offset;i<offset+length;i++)
+        {
+            int bi=0xff&b[i];
+            int c='0'+(bi/16)%16;
+            if (c>'9')
+                c= 'A'+(c-'0'-10);
+            buf.append((char)c);
+            c='0'+bi%16;
+            if (c>'9')
+                c= 'a'+(c-'0'-10);
+            buf.append((char)c);
+        }
+        return buf.toString();
+    }
+
+    /* ------------------------------------------------------------ */
+    public static byte[] fromHexString(String s)
+    {
+        if (s.length()%2!=0)
+            throw new IllegalArgumentException(s);
+        byte[] array = new byte[s.length()/2];
+        for (int i=0;i<array.length;i++)
+        {
+            int b = Integer.parseInt(s.substring(i*2,i*2+2),16);
+            array[i]=(byte)(0xff&b);
+        }
+        return array;
+    }
+
+
+    public static void dump(Class<?> c)
+    {
+        System.err.println("Dump: "+c);
+        dump(c.getClassLoader());
+    }
+
+    public static void dump(ClassLoader cl)
+    {
+        System.err.println("Dump Loaders:");
+        while(cl!=null)
+        {
+            System.err.println("  loader "+cl);
+            cl = cl.getParent();
+        }
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @deprecated
+     */
+    public static byte[] readLine(InputStream in) throws IOException
+    {
+        byte[] buf = new byte[256];
+
+        int i=0;
+        int loops=0;
+        int ch=0;
+
+        while (true)
+        {
+            ch=in.read();
+            if (ch<0)
+                break;
+            loops++;
+
+            // skip a leading LF's
+            if (loops==1 && ch==LF)
+                continue;
+
+            if (ch==CR || ch==LF)
+                break;
+
+            if (i>=buf.length)
+            {
+                byte[] old_buf=buf;
+                buf=new byte[old_buf.length+256];
+                System.arraycopy(old_buf, 0, buf, 0, old_buf.length);
+            }
+            buf[i++]=(byte)ch;
+        }
+
+        if (ch==-1 && i==0)
+            return null;
+
+        // skip a trailing LF if it exists
+        if (ch==CR && in.available()>=1 && in.markSupported())
+        {
+            in.mark(1);
+            ch=in.read();
+            if (ch!=LF)
+                in.reset();
+        }
+
+        byte[] old_buf=buf;
+        buf=new byte[i];
+        System.arraycopy(old_buf, 0, buf, 0, i);
+
+        return buf;
+    }
+
+    public static URL jarFor(String className)
+    {
+        try
+        {
+            className=className.replace('.','/')+".class";
+            // hack to discover jstl libraries
+            URL url = Loader.getResource(null,className,false);
+            String s=url.toString();
+            if (s.startsWith("jar:file:"))
+                return new URL(s.substring(4,s.indexOf("!/")));
+        }
+        catch(Exception e)
+        {
+            LOG.ignore(e);
+        }
+        return null;
+    }
+    
+    public static Object call(Class<?> oClass, String method, Object obj, Object[] arg) 
+       throws InvocationTargetException, NoSuchMethodException
+    {
+        // Lets just try all methods for now
+        Method[] methods = oClass.getMethods();
+        for (int c = 0; methods != null && c < methods.length; c++)
+        {
+            if (!methods[c].getName().equals(method))
+                continue;
+            if (methods[c].getParameterTypes().length != arg.length)
+                continue;
+            if (Modifier.isStatic(methods[c].getModifiers()) != (obj == null))
+                continue;
+            if ((obj == null) && methods[c].getDeclaringClass() != oClass)
+                continue;
+
+            try
+            {
+                return methods[c].invoke(obj,arg);
+            }
+            catch (IllegalAccessException e)
+            {
+                LOG.ignore(e);
+            }
+            catch (IllegalArgumentException e)
+            {
+                LOG.ignore(e);
+            }
+        }
+
+        throw new NoSuchMethodException(method);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/URIUtil.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,692 @@
+//
+//  ========================================================================
+//  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.util;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URLEncoder;
+
+import org.eclipse.jetty.util.log.Log;
+
+
+
+/* ------------------------------------------------------------ */
+/** URI Holder.
+ * This class assists with the decoding and encoding or HTTP URI's.
+ * It differs from the java.net.URL class as it does not provide
+ * communications ability, but it does assist with query string
+ * formatting.
+ * <P>UTF-8 encoding is used by default for % encoded characters. This
+ * may be overridden with the org.eclipse.jetty.util.URI.charset system property.
+ * @see UrlEncoded
+ * 
+ */
+public class URIUtil
+    implements Cloneable
+{
+    public static final String SLASH="/";
+    public static final String HTTP="http";
+    public static final String HTTP_COLON="http:";
+    public static final String HTTPS="https";
+    public static final String HTTPS_COLON="https:";
+
+    // Use UTF-8 as per http://www.w3.org/TR/html40/appendix/notes.html#non-ascii-chars
+    public static final String __CHARSET=System.getProperty("org.eclipse.jetty.util.URI.charset",StringUtil.__UTF8);
+    
+    private URIUtil()
+    {}
+    
+    /* ------------------------------------------------------------ */
+    /** Encode a URI path.
+     * This is the same encoding offered by URLEncoder, except that
+     * the '/' character is not encoded.
+     * @param path The path the encode
+     * @return The encoded path
+     */
+    public static String encodePath(String path)
+    {
+        if (path==null || path.length()==0)
+            return path;
+        
+        StringBuilder buf = encodePath(null,path);
+        return buf==null?path:buf.toString();
+    }
+        
+    /* ------------------------------------------------------------ */
+    /** Encode a URI path.
+     * @param path The path the encode
+     * @param buf StringBuilder to encode path into (or null)
+     * @return The StringBuilder or null if no substitutions required.
+     */
+    public static StringBuilder encodePath(StringBuilder buf, String path)
+    {
+        byte[] bytes=null;
+        if (buf==null)
+        {
+        loop:
+            for (int i=0;i<path.length();i++)
+            {
+                char c=path.charAt(i);
+                switch(c)
+                {
+                    case '%':
+                    case '?':
+                    case ';':
+                    case '#':
+                    case '\'':
+                    case '"':
+                    case '<':
+                    case '>':
+                    case ' ':
+                        buf=new StringBuilder(path.length()*2);
+                        break loop;
+                    default:
+                        if (c>127)
+                        {
+                            try
+                            {
+                                bytes=path.getBytes(URIUtil.__CHARSET);
+                            }
+                            catch (UnsupportedEncodingException e)
+                            {
+                                throw new IllegalStateException(e);
+                            }
+                            buf=new StringBuilder(path.length()*2);
+                            break loop;
+                        }
+                       
+                }
+            }
+            if (buf==null)
+                return null;
+        }
+        
+        synchronized(buf)
+        {
+            if (bytes!=null)
+            {
+                for (int i=0;i<bytes.length;i++)
+                {
+                    byte c=bytes[i];       
+                    switch(c)
+                    {
+                      case '%':
+                          buf.append("%25");
+                          continue;
+                      case '?':
+                          buf.append("%3F");
+                          continue;
+                      case ';':
+                          buf.append("%3B");
+                          continue;
+                      case '#':
+                          buf.append("%23");
+                          continue;
+                      case '"':
+                          buf.append("%22");
+                          continue;
+                      case '\'':
+                          buf.append("%27");
+                          continue;
+                      case '<':
+                          buf.append("%3C");
+                          continue;
+                      case '>':
+                          buf.append("%3E");
+                          continue;
+                      case ' ':
+                          buf.append("%20");
+                          continue;
+                      default:
+                          if (c<0)
+                          {
+                              buf.append('%');
+                              TypeUtil.toHex(c,buf);
+                          }
+                          else
+                              buf.append((char)c);
+                          continue;
+                    }
+                }
+                
+            }
+            else
+            {
+                for (int i=0;i<path.length();i++)
+                {
+                    char c=path.charAt(i);       
+                    switch(c)
+                    {
+                        case '%':
+                            buf.append("%25");
+                            continue;
+                        case '?':
+                            buf.append("%3F");
+                            continue;
+                        case ';':
+                            buf.append("%3B");
+                            continue;
+                        case '#':
+                            buf.append("%23");
+                            continue;
+                        case '"':
+                            buf.append("%22");
+                            continue;
+                        case '\'':
+                            buf.append("%27");
+                            continue;
+                        case '<':
+                            buf.append("%3C");
+                            continue;
+                        case '>':
+                            buf.append("%3E");
+                            continue;
+                        case ' ':
+                            buf.append("%20");
+                            continue;
+                        default:
+                            buf.append(c);
+                            continue;
+                    }
+                }
+            }
+        }
+
+        return buf;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Encode a URI path.
+     * @param path The path the encode
+     * @param buf StringBuilder to encode path into (or null)
+     * @param encode String of characters to encode. % is always encoded.
+     * @return The StringBuilder or null if no substitutions required.
+     */
+    public static StringBuilder encodeString(StringBuilder buf,
+                                             String path,
+                                             String encode)
+    {
+        if (buf==null)
+        {
+        loop:
+            for (int i=0;i<path.length();i++)
+            {
+                char c=path.charAt(i);
+                if (c=='%' || encode.indexOf(c)>=0)
+                {    
+                    buf=new StringBuilder(path.length()<<1);
+                    break loop;
+                }
+            }
+            if (buf==null)
+                return null;
+        }
+        
+        synchronized(buf)
+        {
+            for (int i=0;i<path.length();i++)
+            {
+                char c=path.charAt(i);
+                if (c=='%' || encode.indexOf(c)>=0)
+                {
+                    buf.append('%');
+                    StringUtil.append(buf,(byte)(0xff&c),16);
+                }
+                else
+                    buf.append(c);
+            }
+        }
+
+        return buf;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /* Decode a URI path and strip parameters
+     * @param path The path the encode
+     * @param buf StringBuilder to encode path into
+     */
+    public static String decodePath(String path)
+    {
+        if (path==null)
+            return null;
+        // Array to hold all converted characters
+        char[] chars=null;
+        int n=0;
+        // Array to hold a sequence of %encodings
+        byte[] bytes=null;
+        int b=0;
+        
+        int len=path.length();
+        
+        for (int i=0;i<len;i++)
+        {
+            char c = path.charAt(i);
+
+            if (c=='%' && (i+2)<len)
+            {
+                if (chars==null)
+                {
+                    chars=new char[len];
+                    bytes=new byte[len];
+                    path.getChars(0,i,chars,0);
+                }
+                bytes[b++]=(byte)(0xff&TypeUtil.parseInt(path,i+1,2,16));
+                i+=2;
+                continue;
+            }
+            else if (c==';')
+            {
+                if (chars==null)
+                {
+                    chars=new char[len];
+                    path.getChars(0,i,chars,0);
+                    n=i;
+                }
+                break;
+            }
+            else if (bytes==null)
+            {
+                n++;
+                continue;
+            }
+            
+            // Do we have some bytes to convert?
+            if (b>0)
+            {
+                // convert series of bytes and add to chars
+                String s;
+                try
+                {
+                    s=new String(bytes,0,b,__CHARSET);
+                }
+                catch (UnsupportedEncodingException e)
+                {       
+                    s=new String(bytes,0,b);
+                }
+                s.getChars(0,s.length(),chars,n);
+                n+=s.length();
+                b=0;
+            }
+            
+            chars[n++]=c;
+        }
+
+        if (chars==null)
+            return path;
+
+        // if we have a remaining sequence of bytes
+        if (b>0)
+        {
+            // convert series of bytes and add to chars
+            String s;
+            try
+            {
+                s=new String(bytes,0,b,__CHARSET);
+            }
+            catch (UnsupportedEncodingException e)
+            {       
+                s=new String(bytes,0,b);
+            }
+            s.getChars(0,s.length(),chars,n);
+            n+=s.length();
+        }
+        
+        return new String(chars,0,n);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /* Decode a URI path and strip parameters.
+     * @param path The path the encode
+     * @param buf StringBuilder to encode path into
+     */
+    public static String decodePath(byte[] buf, int offset, int length)
+    {
+        byte[] bytes=null;
+        int n=0;
+        
+        for (int i=0;i<length;i++)
+        {
+            byte b = buf[i + offset];
+            
+            if (b=='%' && (i+2)<length)
+            {
+                b=(byte)(0xff&TypeUtil.parseInt(buf,i+offset+1,2,16));
+                i+=2;
+            }
+            else if (b==';')
+            {
+                length=i;
+                break;
+            }
+            else if (bytes==null)
+            {
+                n++;
+                continue;
+            }
+            
+            if (bytes==null)
+            {
+                bytes=new byte[length];
+                for (int j=0;j<n;j++)
+                    bytes[j]=buf[j + offset];
+            }
+            
+            bytes[n++]=b;
+        }
+
+        if (bytes==null)
+            return StringUtil.toString(buf,offset,length,__CHARSET);
+        return StringUtil.toString(bytes,0,n,__CHARSET);
+    }
+
+    
+    /* ------------------------------------------------------------ */
+    /** Add two URI path segments.
+     * Handles null and empty paths, path and query params (eg ?a=b or
+     * ;JSESSIONID=xxx) and avoids duplicate '/'
+     * @param p1 URI path segment (should be encoded)
+     * @param p2 URI path segment (should be encoded)
+     * @return Legally combined path segments.
+     */
+    public static String addPaths(String p1, String p2)
+    {
+        if (p1==null || p1.length()==0)
+        {
+            if (p1!=null && p2==null)
+                return p1;
+            return p2;
+        }
+        if (p2==null || p2.length()==0)
+            return p1;
+        
+        int split=p1.indexOf(';');
+        if (split<0)
+            split=p1.indexOf('?');
+        if (split==0)
+            return p2+p1;
+        if (split<0)
+            split=p1.length();
+
+        StringBuilder buf = new StringBuilder(p1.length()+p2.length()+2);
+        buf.append(p1);
+        
+        if (buf.charAt(split-1)=='/')
+        {
+            if (p2.startsWith(URIUtil.SLASH))
+            {
+                buf.deleteCharAt(split-1);
+                buf.insert(split-1,p2);
+            }
+            else
+                buf.insert(split,p2);
+        }
+        else
+        {
+            if (p2.startsWith(URIUtil.SLASH))
+                buf.insert(split,p2);
+            else
+            {
+                buf.insert(split,'/');
+                buf.insert(split+1,p2);
+            }
+        }
+
+        return buf.toString();
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Return the parent Path.
+     * Treat a URI like a directory path and return the parent directory.
+     */
+    public static String parentPath(String p)
+    {
+        if (p==null || URIUtil.SLASH.equals(p))
+            return null;
+        int slash=p.lastIndexOf('/',p.length()-2);
+        if (slash>=0)
+            return p.substring(0,slash+1);
+        return null;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Convert a path to a cananonical form.
+     * All instances of "." and ".." are factored out.  Null is returned
+     * if the path tries to .. above its root.
+     * @param path 
+     * @return path or null.
+     */
+    public static String canonicalPath(String path)
+    {
+        if (path==null || path.length()==0)
+            return path;
+
+        int end=path.length();
+        int start = path.lastIndexOf('/', end);
+
+    search:
+        while (end>0)
+        {
+            switch(end-start)
+            {
+              case 2: // possible single dot
+                  if (path.charAt(start+1)!='.')
+                      break;
+                  break search;
+              case 3: // possible double dot
+                  if (path.charAt(start+1)!='.' || path.charAt(start+2)!='.')
+                      break;
+                  break search;
+            }
+            
+            end=start;
+            start=path.lastIndexOf('/',end-1);
+        }
+
+        // If we have checked the entire string
+        if (start>=end)
+            return path;
+        
+        StringBuilder buf = new StringBuilder(path);
+        int delStart=-1;
+        int delEnd=-1;
+        int skip=0;
+        
+        while (end>0)
+        {
+            switch(end-start)
+            {       
+              case 2: // possible single dot
+                  if (buf.charAt(start+1)!='.')
+                  {
+                      if (skip>0 && --skip==0)
+                      {   
+                          delStart=start>=0?start:0;
+                          if(delStart>0 && delEnd==buf.length() && buf.charAt(delEnd-1)=='.')
+                              delStart++;
+                      }
+                      break;
+                  }
+                  
+                  if(start<0 && buf.length()>2 && buf.charAt(1)=='/' && buf.charAt(2)=='/')
+                      break;
+                  
+                  if(delEnd<0)
+                      delEnd=end;
+                  delStart=start;
+                  if (delStart<0 || delStart==0&&buf.charAt(delStart)=='/')
+                  {
+                      delStart++;
+                      if (delEnd<buf.length() && buf.charAt(delEnd)=='/')
+                          delEnd++;
+                      break;
+                  }
+                  if (end==buf.length())
+                      delStart++;
+                  
+                  end=start--;
+                  while (start>=0 && buf.charAt(start)!='/')
+                      start--;
+                  continue;
+                  
+              case 3: // possible double dot
+                  if (buf.charAt(start+1)!='.' || buf.charAt(start+2)!='.')
+                  {
+                      if (skip>0 && --skip==0)
+                      {   delStart=start>=0?start:0;
+                          if(delStart>0 && delEnd==buf.length() && buf.charAt(delEnd-1)=='.')
+                              delStart++;
+                      }
+                      break;
+                  }
+                  
+                  delStart=start;
+                  if (delEnd<0)
+                      delEnd=end;
+
+                  skip++;
+                  end=start--;
+                  while (start>=0 && buf.charAt(start)!='/')
+                      start--;
+                  continue;
+
+              default:
+                  if (skip>0 && --skip==0)
+                  {
+                      delStart=start>=0?start:0;
+                      if(delEnd==buf.length() && buf.charAt(delEnd-1)=='.')
+                          delStart++;
+                  }
+            }     
+            
+            // Do the delete
+            if (skip<=0 && delStart>=0 && delEnd>=delStart)
+            {  
+                buf.delete(delStart,delEnd);
+                delStart=delEnd=-1;
+                if (skip>0)
+                    delEnd=end;
+            }
+            
+            end=start--;
+            while (start>=0 && buf.charAt(start)!='/')
+                start--;
+        }      
+
+        // Too many ..
+        if (skip>0)
+            return null;
+        
+        // Do the delete
+        if (delEnd>=0)
+            buf.delete(delStart,delEnd);
+
+        return buf.toString();
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Convert a path to a compact form.
+     * All instances of "//" and "///" etc. are factored out to single "/" 
+     * @param path 
+     * @return path
+     */
+    public static String compactPath(String path)
+    {
+        if (path==null || path.length()==0)
+            return path;
+
+        int state=0;
+        int end=path.length();
+        int i=0;
+        
+        loop:
+        while (i<end)
+        {
+            char c=path.charAt(i);
+            switch(c)
+            {
+                case '?':
+                    return path;
+                case '/':
+                    state++;
+                    if (state==2)
+                        break loop;
+                    break;
+                default:
+                    state=0;
+            }
+            i++;
+        }
+        
+        if (state<2)
+            return path;
+        
+        StringBuffer buf = new StringBuffer(path.length());
+        buf.append(path,0,i);
+        
+        loop2:
+        while (i<end)
+        {
+            char c=path.charAt(i);
+            switch(c)
+            {
+                case '?':
+                    buf.append(path,i,end);
+                    break loop2;
+                case '/':
+                    if (state++==0)
+                        buf.append(c);
+                    break;
+                default:
+                    state=0;
+                    buf.append(c);
+            }
+            i++;
+        }
+        
+        return buf.toString();
+    }
+
+    /* ------------------------------------------------------------ */
+    /** 
+     * @param uri URI
+     * @return True if the uri has a scheme
+     */
+    public static boolean hasScheme(String uri)
+    {
+        for (int i=0;i<uri.length();i++)
+        {
+            char c=uri.charAt(i);
+            if (c==':')
+                return true;
+            if (!(c>='a'&&c<='z' ||
+                  c>='A'&&c<='Z' ||
+                  (i>0 &&(c>='0'&&c<='9' ||
+                          c=='.' ||
+                          c=='+' ||
+                          c=='-'))
+                  ))
+                break;
+        }
+        return false;
+    }
+    
+}
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/UrlEncoded.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,1034 @@
+//
+//  ========================================================================
+//  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.util;
+
+import static org.eclipse.jetty.util.TypeUtil.convertHexDigit;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.eclipse.jetty.util.Utf8Appendable.NotUtf8Exception;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/* ------------------------------------------------------------ */
+/** Handles coding of MIME  "x-www-form-urlencoded".
+ * <p>
+ * This class handles the encoding and decoding for either
+ * the query string of a URL or the _content of a POST HTTP request.
+ *
+ * <h4>Notes</h4>
+ * The UTF-8 charset is assumed, unless otherwise defined by either
+ * passing a parameter or setting the "org.eclipse.jetty.util.UrlEncoding.charset"
+ * System property.
+ * <p>
+ * The hashtable either contains String single values, vectors
+ * of String or arrays of Strings.
+ * <p>
+ * This class is only partially synchronised.  In particular, simple
+ * get operations are not protected from concurrent updates.
+ *
+ * @see java.net.URLEncoder
+ */
+public class UrlEncoded extends MultiMap implements Cloneable
+{
+    private static final Logger LOG = Log.getLogger(UrlEncoded.class);
+
+    public static final String ENCODING = System.getProperty("org.eclipse.jetty.util.UrlEncoding.charset",StringUtil.__UTF8);
+
+    /* ----------------------------------------------------------------- */
+    public UrlEncoded(UrlEncoded url)
+    {
+        super(url);
+    }
+    
+    /* ----------------------------------------------------------------- */
+    public UrlEncoded()
+    {
+        super(6);
+    }
+    
+    /* ----------------------------------------------------------------- */
+    public UrlEncoded(String s)
+    {
+        super(6);
+        decode(s,ENCODING);
+    }
+    
+    /* ----------------------------------------------------------------- */
+    public UrlEncoded(String s, String charset)
+    {
+        super(6);
+        decode(s,charset);
+    }
+    
+    /* ----------------------------------------------------------------- */
+    public void decode(String query)
+    {
+        decodeTo(query,this,ENCODING,-1);
+    }
+    
+    /* ----------------------------------------------------------------- */
+    public void decode(String query,String charset)
+    {
+        decodeTo(query,this,charset,-1);
+    }
+    
+    /* -------------------------------------------------------------- */
+    /** Encode Hashtable with % encoding.
+     */
+    public String encode()
+    {
+        return encode(ENCODING,false);
+    }
+    
+    /* -------------------------------------------------------------- */
+    /** Encode Hashtable with % encoding.
+     */
+    public String encode(String charset)
+    {
+        return encode(charset,false);
+    }
+    
+    /* -------------------------------------------------------------- */
+    /** Encode Hashtable with % encoding.
+     * @param equalsForNullValue if True, then an '=' is always used, even
+     * for parameters without a value. e.g. "blah?a=&b=&c=".
+     */
+    public synchronized String encode(String charset, boolean equalsForNullValue)
+    {
+        return encode(this,charset,equalsForNullValue);
+    }
+    
+    /* -------------------------------------------------------------- */
+    /** Encode Hashtable with % encoding.
+     * @param equalsForNullValue if True, then an '=' is always used, even
+     * for parameters without a value. e.g. "blah?a=&b=&c=".
+     */
+    public static String encode(MultiMap map, String charset, boolean equalsForNullValue)
+    {
+        if (charset==null)
+            charset=ENCODING;
+
+        StringBuilder result = new StringBuilder(128);
+
+        Iterator iter = map.entrySet().iterator();
+        while(iter.hasNext())
+        {
+            Map.Entry entry = (Map.Entry)iter.next();
+
+            String key = entry.getKey().toString();
+            Object list = entry.getValue();
+            int s=LazyList.size(list);
+
+            if (s==0)
+            {
+                result.append(encodeString(key,charset));
+                if(equalsForNullValue)
+                    result.append('=');
+            }
+            else
+            {
+                for (int i=0;i<s;i++)
+                {
+                    if (i>0)
+                        result.append('&');
+                    Object val=LazyList.get(list,i);
+                    result.append(encodeString(key,charset));
+
+                    if (val!=null)
+                    {
+                        String str=val.toString();
+                        if (str.length()>0)
+                        {
+                            result.append('=');
+                            result.append(encodeString(str,charset));
+                        }
+                        else if (equalsForNullValue)
+                            result.append('=');
+                    }
+                    else if (equalsForNullValue)
+                        result.append('=');
+                }
+            }
+            if (iter.hasNext())
+                result.append('&');
+        }
+        return result.toString();
+    }
+
+
+
+    /* -------------------------------------------------------------- */
+    /** Decoded parameters to Map.
+     * @param content the string containing the encoded parameters
+     */
+    public static void decodeTo(String content, MultiMap map, String charset)
+    {
+        decodeTo(content,map,charset,-1);
+    }
+    
+    /* -------------------------------------------------------------- */
+    /** Decoded parameters to Map.
+     * @param content the string containing the encoded parameters
+     */
+    public static void decodeTo(String content, MultiMap map, String charset, int maxKeys)
+    {
+        if (charset==null)
+            charset=ENCODING;
+
+        synchronized(map)
+        {
+            String key = null;
+            String value = null;
+            int mark=-1;
+            boolean encoded=false;
+            for (int i=0;i<content.length();i++)
+            {
+                char c = content.charAt(i);
+                switch (c)
+                {
+                  case '&':
+                      int l=i-mark-1;
+                      value = l==0?"":
+                          (encoded?decodeString(content,mark+1,l,charset):content.substring(mark+1,i));
+                      mark=i;
+                      encoded=false;
+                      if (key != null)
+                      {
+                          map.add(key,value);
+                      }
+                      else if (value!=null&&value.length()>0)
+                      {
+                          map.add(value,"");
+                      }
+                      key = null;
+                      value=null;
+                      if (maxKeys>0 && map.size()>maxKeys)
+                          throw new IllegalStateException("Form too many keys");
+                      break;
+                  case '=':
+                      if (key!=null)
+                          break;
+                      key = encoded?decodeString(content,mark+1,i-mark-1,charset):content.substring(mark+1,i);
+                      mark=i;
+                      encoded=false;
+                      break;
+                  case '+':
+                      encoded=true;
+                      break;
+                  case '%':
+                      encoded=true;
+                      break;
+                }                
+            }
+            
+            if (key != null)
+            {
+                int l=content.length()-mark-1;
+                value = l==0?"":(encoded?decodeString(content,mark+1,l,charset):content.substring(mark+1));
+                map.add(key,value);
+            }
+            else if (mark<content.length())
+            {
+                key = encoded
+                    ?decodeString(content,mark+1,content.length()-mark-1,charset)
+                    :content.substring(mark+1);
+                if (key != null && key.length() > 0)
+                {
+                    map.add(key,"");
+                }
+            }
+        }
+    }
+
+    /* -------------------------------------------------------------- */
+    /** Decoded parameters to Map.
+     * @param raw the byte[] containing the encoded parameters
+     * @param offset the offset within raw to decode from
+     * @param length the length of the section to decode
+     * @param map the {@link MultiMap} to populate
+     */
+    public static void decodeUtf8To(byte[] raw,int offset, int length, MultiMap map)
+    {
+        decodeUtf8To(raw,offset,length,map,new Utf8StringBuilder());
+    }
+
+    /* -------------------------------------------------------------- */
+    /** Decoded parameters to Map.
+     * @param raw the byte[] containing the encoded parameters
+     * @param offset the offset within raw to decode from
+     * @param length the length of the section to decode
+     * @param map the {@link MultiMap} to populate
+     * @param buffer the buffer to decode into
+     */
+    public static void decodeUtf8To(byte[] raw,int offset, int length, MultiMap map,Utf8StringBuilder buffer)
+    {
+        synchronized(map)
+        {
+            String key = null;
+            String value = null;
+
+            // TODO cache of parameter names ???
+            int end=offset+length;
+            for (int i=offset;i<end;i++)
+            {
+                byte b=raw[i];
+                try
+                {
+                    switch ((char)(0xff&b))
+                    {
+                        case '&':
+                            value = buffer.length()==0?"":buffer.toString();
+                            buffer.reset();
+                            if (key != null)
+                            {
+                                map.add(key,value);
+                            }
+                            else if (value!=null&&value.length()>0)
+                            {
+                                map.add(value,"");
+                            }
+                            key = null;
+                            value=null;
+                            break;
+
+                        case '=':
+                            if (key!=null)
+                            {
+                                buffer.append(b);
+                                break;
+                            }
+                            key = buffer.toString();
+                            buffer.reset();
+                            break;
+
+                        case '+':
+                            buffer.append((byte)' ');
+                            break;
+
+                        case '%':
+                            if (i+2<end)
+                            {
+                                if ('u'==raw[i+1])
+                                {
+                                    i++;
+                                    if (i+4<end)
+                                        buffer.getStringBuilder().append(Character.toChars((convertHexDigit(raw[++i])<<12) +(convertHexDigit(raw[++i])<<8) + (convertHexDigit(raw[++i])<<4) +convertHexDigit(raw[++i])));
+                                    else
+                                    {
+                                        buffer.getStringBuilder().append(Utf8Appendable.REPLACEMENT);
+                                        i=end;
+                                    }
+                                }
+                                else
+                                    buffer.append((byte)((convertHexDigit(raw[++i])<<4) + convertHexDigit(raw[++i])));
+                            }
+                            else
+                            {
+                                buffer.getStringBuilder().append(Utf8Appendable.REPLACEMENT);
+                                i=end;
+                            }
+                            break;
+                            
+                        default:
+                            buffer.append(b);
+                            break;
+                    }
+                }
+                catch(NotUtf8Exception e)
+                {
+                    LOG.warn(e.toString());
+                    LOG.debug(e);
+                }
+            }
+            
+            if (key != null)
+            {
+                value = buffer.length()==0?"":buffer.toReplacedString();
+                buffer.reset();
+                map.add(key,value);
+            }
+            else if (buffer.length()>0)
+            {
+                map.add(buffer.toReplacedString(),"");
+            }
+        }
+    }
+
+    /* -------------------------------------------------------------- */
+    /** Decoded parameters to Map.
+     * @param in InputSteam to read
+     * @param map MultiMap to add parameters to
+     * @param maxLength maximum number of keys to read or -1 for no limit
+     */
+    public static void decode88591To(InputStream in, MultiMap map, int maxLength, int maxKeys)
+    throws IOException
+    {
+        synchronized(map)
+        {
+            StringBuffer buffer = new StringBuffer();
+            String key = null;
+            String value = null;
+            
+            int b;
+
+            // TODO cache of parameter names ???
+            int totalLength=0;
+            while ((b=in.read())>=0)
+            {
+                switch ((char) b)
+                {
+                    case '&':
+                        value = buffer.length()==0?"":buffer.toString();
+                        buffer.setLength(0);
+                        if (key != null)
+                        {
+                            map.add(key,value);
+                        }
+                        else if (value!=null&&value.length()>0)
+                        {
+                            map.add(value,"");
+                        }
+                        key = null;
+                        value=null;
+                        if (maxKeys>0 && map.size()>maxKeys)
+                            throw new IllegalStateException("Form too many keys");
+                        break;
+                        
+                    case '=':
+                        if (key!=null)
+                        {
+                            buffer.append((char)b);
+                            break;
+                        }
+                        key = buffer.toString();
+                        buffer.setLength(0);
+                        break;
+                        
+                    case '+':
+                        buffer.append(' ');
+                        break;
+                        
+                    case '%':
+                        int code0=in.read();
+                        if ('u'==code0)
+                        {
+                            int code1=in.read();
+                            if (code1>=0)
+                            {
+                                int code2=in.read();
+                                if (code2>=0)
+                                {
+                                    int code3=in.read();
+                                    if (code3>=0)
+                                        buffer.append(Character.toChars((convertHexDigit(code0)<<12)+(convertHexDigit(code1)<<8)+(convertHexDigit(code2)<<4)+convertHexDigit(code3)));
+                                }
+                            }
+                        }
+                        else if (code0>=0)
+                        {
+                            int code1=in.read();
+                            if (code1>=0)
+                                buffer.append((char)((convertHexDigit(code0)<<4)+convertHexDigit(code1)));
+                        }
+                        break;
+                     
+                    default:
+                        buffer.append((char)b);
+                    break;
+                }
+                if (maxLength>=0 && (++totalLength > maxLength))
+                    throw new IllegalStateException("Form too large");
+            }
+            
+            if (key != null)
+            {
+                value = buffer.length()==0?"":buffer.toString();
+                buffer.setLength(0);
+                map.add(key,value);
+            }
+            else if (buffer.length()>0)
+            {
+                map.add(buffer.toString(), "");
+            }
+        }
+    }
+    
+    /* -------------------------------------------------------------- */
+    /** Decoded parameters to Map.
+     * @param in InputSteam to read
+     * @param map MultiMap to add parameters to
+     * @param maxLength maximum number of keys to read or -1 for no limit
+     */
+    public static void decodeUtf8To(InputStream in, MultiMap map, int maxLength, int maxKeys)
+    throws IOException
+    {
+        synchronized(map)
+        {
+            Utf8StringBuilder buffer = new Utf8StringBuilder();
+            String key = null;
+            String value = null;
+            
+            int b;
+            
+            // TODO cache of parameter names ???
+            int totalLength=0;
+            while ((b=in.read())>=0)
+            {
+                try
+                {
+                    switch ((char) b)
+                    {
+                        case '&':
+                            value = buffer.length()==0?"":buffer.toString();
+                            buffer.reset();
+                            if (key != null)
+                            {
+                                map.add(key,value);
+                            }
+                            else if (value!=null&&value.length()>0)
+                            {
+                                map.add(value,"");
+                            }
+                            key = null;
+                            value=null;
+                            if (maxKeys>0 && map.size()>maxKeys)
+                                throw new IllegalStateException("Form too many keys");
+                            break;
+
+                        case '=':
+                            if (key!=null)
+                            {
+                                buffer.append((byte)b);
+                                break;
+                            }
+                            key = buffer.toString();
+                            buffer.reset();
+                            break;
+
+                        case '+':
+                            buffer.append((byte)' ');
+                            break;
+
+                        case '%':
+                            int code0=in.read();
+                            if ('u'==code0)
+                            {
+                                int code1=in.read();
+                                if (code1>=0)
+                                {
+                                    int code2=in.read();
+                                    if (code2>=0)
+                                    {
+                                        int code3=in.read();
+                                        if (code3>=0)
+                                            buffer.getStringBuilder().append(Character.toChars((convertHexDigit(code0)<<12)+(convertHexDigit(code1)<<8)+(convertHexDigit(code2)<<4)+convertHexDigit(code3)));
+                                    }
+                                }
+                            }
+                            else if (code0>=0)
+                            {
+                                int code1=in.read();
+                                if (code1>=0)
+                                    buffer.append((byte)((convertHexDigit(code0)<<4)+convertHexDigit(code1)));
+                            }
+                            break;
+                          
+                        default:
+                            buffer.append((byte)b);
+                            break;
+                    }
+                }
+                catch(NotUtf8Exception e)
+                {
+                    LOG.warn(e.toString());
+                    LOG.debug(e);
+                }
+                if (maxLength>=0 && (++totalLength > maxLength))
+                    throw new IllegalStateException("Form too large");
+            }
+            
+            if (key != null)
+            {
+                value = buffer.length()==0?"":buffer.toString();
+                buffer.reset();
+                map.add(key,value);
+            }
+            else if (buffer.length()>0)
+            {
+                map.add(buffer.toString(), "");
+            }
+        }
+    }
+    
+    /* -------------------------------------------------------------- */
+    public static void decodeUtf16To(InputStream in, MultiMap map, int maxLength, int maxKeys) throws IOException
+    {
+        InputStreamReader input = new InputStreamReader(in,StringUtil.__UTF16);
+        StringWriter buf = new StringWriter(8192);
+        IO.copy(input,buf,maxLength);
+        
+        decodeTo(buf.getBuffer().toString(),map,StringUtil.__UTF16,maxKeys);
+    }
+    
+    /* -------------------------------------------------------------- */
+    /** Decoded parameters to Map.
+     * @param in the stream containing the encoded parameters
+     */
+    public static void decodeTo(InputStream in, MultiMap map, String charset, int maxLength, int maxKeys)
+    throws IOException
+    {
+        //no charset present, use the configured default
+        if (charset==null) 
+        {
+           charset=ENCODING;
+        }
+            
+        if (StringUtil.__UTF8.equalsIgnoreCase(charset))
+        {
+            decodeUtf8To(in,map,maxLength,maxKeys);
+            return;
+        }
+        
+        if (StringUtil.__ISO_8859_1.equals(charset))
+        {
+            decode88591To(in,map,maxLength,maxKeys);
+            return;
+        }
+
+        if (StringUtil.__UTF16.equalsIgnoreCase(charset)) // Should be all 2 byte encodings
+        {
+            decodeUtf16To(in,map,maxLength,maxKeys);
+            return;
+        }
+        
+
+        synchronized(map)
+        {
+            String key = null;
+            String value = null;
+            
+            int c;
+            
+            int totalLength = 0;
+            ByteArrayOutputStream2 output = new ByteArrayOutputStream2();
+            
+            int size=0;
+            
+            while ((c=in.read())>0)
+            {
+                switch ((char) c)
+                {
+                    case '&':
+                        size=output.size();
+                        value = size==0?"":output.toString(charset);
+                        output.setCount(0);
+                        if (key != null)
+                        {
+                            map.add(key,value);
+                        }
+                        else if (value!=null&&value.length()>0)
+                        {
+                            map.add(value,"");
+                        }
+                        key = null;
+                        value=null;
+                        if (maxKeys>0 && map.size()>maxKeys)
+                            throw new IllegalStateException("Form too many keys");
+                        break;
+                    case '=':
+                        if (key!=null)
+                        {
+                            output.write(c);
+                            break;
+                        }
+                        size=output.size();
+                        key = size==0?"":output.toString(charset);
+                        output.setCount(0);
+                        break;
+                    case '+':
+                        output.write(' ');
+                        break;
+                    case '%':
+                        int code0=in.read();
+                        if ('u'==code0)
+                        {
+                            int code1=in.read();
+                            if (code1>=0)
+                            {
+                                int code2=in.read();
+                                if (code2>=0)
+                                {
+                                    int code3=in.read();
+                                    if (code3>=0)
+                                        output.write(new String(Character.toChars((convertHexDigit(code0)<<12)+(convertHexDigit(code1)<<8)+(convertHexDigit(code2)<<4)+convertHexDigit(code3))).getBytes(charset));
+                                }
+                            }
+                            
+                        }
+                        else if (code0>=0)
+                        {
+                            int code1=in.read();
+                            if (code1>=0)
+                                output.write((convertHexDigit(code0)<<4)+convertHexDigit(code1));
+                        }
+                        break;
+                    default:
+                        output.write(c);
+                    break;
+                }
+                
+                totalLength++;
+                if (maxLength>=0 && totalLength > maxLength)
+                    throw new IllegalStateException("Form too large");
+            }
+
+            size=output.size();
+            if (key != null)
+            {
+                value = size==0?"":output.toString(charset);
+                output.setCount(0);
+                map.add(key,value);
+            }
+            else if (size>0)
+                map.add(output.toString(charset),"");
+        }
+    }
+    
+    /* -------------------------------------------------------------- */
+    /** Decode String with % encoding.
+     * This method makes the assumption that the majority of calls
+     * will need no decoding.
+     */
+    public static String decodeString(String encoded,int offset,int length,String charset)
+    {
+        if (charset==null || StringUtil.isUTF8(charset))
+        {
+            Utf8StringBuffer buffer=null;
+
+            for (int i=0;i<length;i++)
+            {
+                char c = encoded.charAt(offset+i);
+                if (c<0||c>0xff)
+                {
+                    if (buffer==null)
+                    {
+                        buffer=new Utf8StringBuffer(length);
+                        buffer.getStringBuffer().append(encoded,offset,offset+i+1);
+                    }
+                    else
+                        buffer.getStringBuffer().append(c);
+                }
+                else if (c=='+')
+                {
+                    if (buffer==null)
+                    {
+                        buffer=new Utf8StringBuffer(length);
+                        buffer.getStringBuffer().append(encoded,offset,offset+i);
+                    }
+                    
+                    buffer.getStringBuffer().append(' ');
+                }
+                else if (c=='%')
+                {
+                    if (buffer==null)
+                    {
+                        buffer=new Utf8StringBuffer(length);
+                        buffer.getStringBuffer().append(encoded,offset,offset+i);
+                    }
+                    
+                    if ((i+2)<length)
+                    {
+                        try
+                        {
+                            if ('u'==encoded.charAt(offset+i+1))
+                            {
+                                if((i+5)<length)
+                                {
+                                    int o=offset+i+2;
+                                    i+=5;
+                                    String unicode = new String(Character.toChars(TypeUtil.parseInt(encoded,o,4,16)));
+                                    buffer.getStringBuffer().append(unicode); 
+                                }
+                                else
+                                {
+                                    i=length;
+                                    buffer.getStringBuffer().append(Utf8Appendable.REPLACEMENT); 
+                                }
+                            }
+                            else
+                            {
+                                int o=offset+i+1;
+                                i+=2;
+                                byte b=(byte)TypeUtil.parseInt(encoded,o,2,16);
+                                buffer.append(b);
+                            }
+                        }
+                        catch(NotUtf8Exception e)
+                        {
+                            LOG.warn(e.toString());
+                            LOG.debug(e);
+                        }
+                        catch(NumberFormatException nfe)
+                        {
+                            LOG.debug(nfe);
+                            buffer.getStringBuffer().append(Utf8Appendable.REPLACEMENT);  
+                        }
+                    }
+                    else
+                    {
+                        buffer.getStringBuffer().append(Utf8Appendable.REPLACEMENT); 
+                        i=length;
+                    }
+                }
+                else if (buffer!=null)
+                    buffer.getStringBuffer().append(c);
+            }
+
+            if (buffer==null)
+            {
+                if (offset==0 && encoded.length()==length)
+                    return encoded;
+                return encoded.substring(offset,offset+length);
+            }
+
+            return buffer.toReplacedString();
+        }
+        else
+        {
+            StringBuffer buffer=null;
+
+            try
+            {
+                for (int i=0;i<length;i++)
+                {
+                    char c = encoded.charAt(offset+i);
+                    if (c<0||c>0xff)
+                    {
+                        if (buffer==null)
+                        {
+                            buffer=new StringBuffer(length);
+                            buffer.append(encoded,offset,offset+i+1);
+                        }
+                        else
+                            buffer.append(c);
+                    }
+                    else if (c=='+')
+                    {
+                        if (buffer==null)
+                        {
+                            buffer=new StringBuffer(length);
+                            buffer.append(encoded,offset,offset+i);
+                        }
+                        
+                        buffer.append(' ');
+                    }
+                    else if (c=='%')
+                    {
+                        if (buffer==null)
+                        {
+                            buffer=new StringBuffer(length);
+                            buffer.append(encoded,offset,offset+i);
+                        }
+
+                        byte[] ba=new byte[length];
+                        int n=0;
+                        while(c>=0 && c<=0xff)
+                        {
+                            if (c=='%')
+                            {   
+                                if(i+2<length)
+                                {
+                                    try
+                                    {
+                                        if ('u'==encoded.charAt(offset+i+1))
+                                        {
+                                            if (i+6<length)
+                                            {
+                                                int o=offset+i+2;
+                                                i+=6;
+                                                String unicode = new String(Character.toChars(TypeUtil.parseInt(encoded,o,4,16)));
+                                                byte[] reencoded = unicode.getBytes(charset);
+                                                System.arraycopy(reencoded,0,ba,n,reencoded.length);
+                                                n+=reencoded.length;
+                                            }
+                                            else
+                                            {
+                                                ba[n++] = (byte)'?';
+                                                i=length;
+                                            }
+                                        }
+                                        else
+                                        {
+                                            int o=offset+i+1;
+                                            i+=3;
+                                            ba[n]=(byte)TypeUtil.parseInt(encoded,o,2,16);
+                                            n++;
+                                        }
+                                    }
+                                    catch(NumberFormatException nfe)
+                                    {   
+                                        LOG.ignore(nfe);
+                                        ba[n++] = (byte)'?';
+                                    }
+                                }
+                                else
+                                {
+                                    ba[n++] = (byte)'?';
+                                    i=length;
+                                }
+                            }
+                            else if (c=='+')
+                            {
+                                ba[n++]=(byte)' ';
+                                i++;
+                            }
+                            else
+                            {
+                                ba[n++]=(byte)c;
+                                i++;
+                            }
+                            
+                            if (i>=length)
+                                break;
+                            c = encoded.charAt(offset+i);
+                        }
+
+                        i--;
+                        buffer.append(new String(ba,0,n,charset));
+
+                    }
+                    else if (buffer!=null)
+                        buffer.append(c);
+                }
+
+                if (buffer==null)
+                {
+                    if (offset==0 && encoded.length()==length)
+                        return encoded;
+                    return encoded.substring(offset,offset+length);
+                }
+
+                return buffer.toString();
+            }
+            catch (UnsupportedEncodingException e)
+            {
+                throw new RuntimeException(e);
+            }
+        }
+        
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Perform URL encoding.
+     * @param string 
+     * @return encoded string.
+     */
+    public static String encodeString(String string)
+    {
+        return encodeString(string,ENCODING);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Perform URL encoding.
+     * @param string 
+     * @return encoded string.
+     */
+    public static String encodeString(String string,String charset)
+    {
+        if (charset==null)
+            charset=ENCODING;
+        byte[] bytes=null;
+        try
+        {
+            bytes=string.getBytes(charset);
+        }
+        catch(UnsupportedEncodingException e)
+        {
+            // LOG.warn(LogSupport.EXCEPTION,e);
+            bytes=string.getBytes();
+        }
+        
+        int len=bytes.length;
+        byte[] encoded= new byte[bytes.length*3];
+        int n=0;
+        boolean noEncode=true;
+        
+        for (int i=0;i<len;i++)
+        {
+            byte b = bytes[i];
+            
+            if (b==' ')
+            {
+                noEncode=false;
+                encoded[n++]=(byte)'+';
+            }
+            else if (b>='a' && b<='z' ||
+                     b>='A' && b<='Z' ||
+                     b>='0' && b<='9')
+            {
+                encoded[n++]=b;
+            }
+            else
+            {
+                noEncode=false;
+                encoded[n++]=(byte)'%';
+                byte nibble= (byte) ((b&0xf0)>>4);
+                if (nibble>=10)
+                    encoded[n++]=(byte)('A'+nibble-10);
+                else
+                    encoded[n++]=(byte)('0'+nibble);
+                nibble= (byte) (b&0xf);
+                if (nibble>=10)
+                    encoded[n++]=(byte)('A'+nibble-10);
+                else
+                    encoded[n++]=(byte)('0'+nibble);
+            }
+        }
+
+        if (noEncode)
+            return string;
+        
+        try
+        {    
+            return new String(encoded,0,n,charset);
+        }
+        catch(UnsupportedEncodingException e)
+        {
+            // LOG.warn(LogSupport.EXCEPTION,e);
+            return new String(encoded,0,n);
+        }
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /** 
+     */
+    @Override
+    public Object clone()
+    {
+        return new UrlEncoded(this);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/Utf8Appendable.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,238 @@
+//
+//  ========================================================================
+//  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.util;
+
+import java.io.IOException;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/* ------------------------------------------------------------ */
+/**
+ * Utf8 Appendable abstract base class
+ *
+ * This abstract class wraps a standard {@link java.lang.Appendable} and provides methods to append UTF-8 encoded bytes, that are converted into characters.
+ *
+ * This class is stateful and up to 4 calls to {@link #append(byte)} may be needed before state a character is appended to the string buffer.
+ *
+ * The UTF-8 decoding is done by this class and no additional buffers or Readers are used. The UTF-8 code was inspired by
+ * http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
+ *
+ * License information for Bjoern Hoehrmann's code:
+ *
+ * Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ **/
+public abstract class Utf8Appendable
+{
+    protected static final Logger LOG = Log.getLogger(Utf8Appendable.class);
+    public static final char REPLACEMENT = '\ufffd';
+    private static final int UTF8_ACCEPT = 0;
+    private static final int UTF8_REJECT = 12;
+
+    protected final Appendable _appendable;
+    protected int _state = UTF8_ACCEPT;
+
+    private static final byte[] BYTE_TABLE =
+    {
+        // The first part of the table maps bytes to character classes that
+        // to reduce the size of the transition table and create bitmasks.
+         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+         1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,  9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
+         7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+         8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+        10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8
+    };
+
+    private static final byte[] TRANS_TABLE =
+    {
+        // The second part is a transition table that maps a combination
+        // of a state of the automaton and a character class to a state.
+         0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
+        12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
+        12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
+        12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
+        12,36,12,12,12,12,12,12,12,12,12,12
+    };
+
+    private int _codep;
+
+    public Utf8Appendable(Appendable appendable)
+    {
+        _appendable = appendable;
+    }
+
+    public abstract int length();
+
+    protected void reset()
+    {
+        _state = UTF8_ACCEPT;
+    }
+
+    public void append(byte b)
+    {
+        try
+        {
+            appendByte(b);
+        }
+        catch (IOException e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public void append(byte[] b, int offset, int length)
+    {
+        try
+        {
+            int end = offset + length;
+            for (int i = offset; i < end; i++)
+                appendByte(b[i]);
+        }
+        catch (IOException e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public boolean append(byte[] b, int offset, int length, int maxChars)
+    {
+        try
+        {
+            int end = offset + length;
+            for (int i = offset; i < end; i++)
+            {
+                if (length() > maxChars)
+                    return false;
+                appendByte(b[i]);
+            }
+            return true;
+        }
+        catch (IOException e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+
+    protected void appendByte(byte b) throws IOException
+    {
+
+        if (b > 0 && _state == UTF8_ACCEPT)
+        {
+            _appendable.append((char)(b & 0xFF));
+        }
+        else
+        {
+            int i = b & 0xFF;
+            int type = BYTE_TABLE[i];
+            _codep = _state == UTF8_ACCEPT ? (0xFF >> type) & i : (i & 0x3F) | (_codep << 6);
+            int next = TRANS_TABLE[_state + type];
+
+            switch(next)
+            {
+                case UTF8_ACCEPT:
+                    _state=next;
+                    if (_codep < Character.MIN_HIGH_SURROGATE)
+                    {
+                        _appendable.append((char)_codep);
+                    }
+                    else
+                    {
+                        for (char c : Character.toChars(_codep))
+                            _appendable.append(c);
+                    }
+                    break;
+                    
+                case UTF8_REJECT:
+                    String reason = "byte "+TypeUtil.toHexString(b)+" in state "+(_state/12);
+                    _codep=0;
+                    _state = UTF8_ACCEPT;
+                    _appendable.append(REPLACEMENT);
+                    throw new NotUtf8Exception(reason);
+                    
+                default:
+                    _state=next;
+                    
+            }
+        }
+    }
+
+    public boolean isUtf8SequenceComplete()
+    {
+        return _state == UTF8_ACCEPT;
+    }
+
+    public static class NotUtf8Exception extends IllegalArgumentException
+    {
+        public NotUtf8Exception(String reason)
+        {
+            super("Not valid UTF8! "+reason);
+        }
+    }
+
+    protected void checkState()
+    {
+        if (!isUtf8SequenceComplete())
+        {
+            _codep=0;
+            _state = UTF8_ACCEPT;
+            try
+            {
+                _appendable.append(REPLACEMENT);
+            }
+            catch(IOException e)
+            {
+                throw new RuntimeException(e);
+            }
+            throw new NotUtf8Exception("incomplete UTF8 sequence");
+        }
+    }
+    
+    public String toReplacedString()
+    {
+        if (!isUtf8SequenceComplete())
+        {
+            _codep=0;
+            _state = UTF8_ACCEPT;
+            try
+            {
+                _appendable.append(REPLACEMENT);
+            }
+            catch(IOException e)
+            {
+                throw new RuntimeException(e);
+            }
+            Throwable th= new NotUtf8Exception("incomplete UTF8 sequence");
+            LOG.warn(th.toString());
+            LOG.debug(th);
+        }
+        return _appendable.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/Utf8StringBuffer.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,75 @@
+//
+//  ========================================================================
+//  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.util;
+
+/* ------------------------------------------------------------ */
+/**
+ * UTF-8 StringBuffer.
+ *
+ * This class wraps a standard {@link java.lang.StringBuffer} and provides methods to append
+ * UTF-8 encoded bytes, that are converted into characters.
+ *
+ * This class is stateful and up to 4 calls to {@link #append(byte)} may be needed before
+ * state a character is appended to the string buffer.
+ *
+ * The UTF-8 decoding is done by this class and no additional buffers or Readers are used.
+ * The UTF-8 code was inspired by http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
+ */
+public class Utf8StringBuffer extends Utf8Appendable
+{
+    final StringBuffer _buffer;
+
+    public Utf8StringBuffer()
+    {
+        super(new StringBuffer());
+        _buffer = (StringBuffer)_appendable;
+    }
+
+    public Utf8StringBuffer(int capacity)
+    {
+        super(new StringBuffer(capacity));
+        _buffer = (StringBuffer)_appendable;
+    }
+
+    @Override
+    public int length()
+    {
+        return _buffer.length();
+    }
+
+    @Override
+    public void reset()
+    {
+        super.reset();
+        _buffer.setLength(0);
+    }
+
+    public StringBuffer getStringBuffer()
+    {
+        checkState();
+        return _buffer;
+    }
+
+    @Override
+    public String toString()
+    {
+        checkState();
+        return _buffer.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/Utf8StringBuilder.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,78 @@
+//
+//  ========================================================================
+//  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.util;
+
+
+/* ------------------------------------------------------------ */
+/** UTF-8 StringBuilder.
+ *
+ * This class wraps a standard {@link java.lang.StringBuilder} and provides methods to append
+ * UTF-8 encoded bytes, that are converted into characters.
+ *
+ * This class is stateful and up to 4 calls to {@link #append(byte)} may be needed before
+ * state a character is appended to the string buffer.
+ *
+ * The UTF-8 decoding is done by this class and no additional buffers or Readers are used.
+ * The UTF-8 code was inspired by http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
+ *
+ */
+public class Utf8StringBuilder extends Utf8Appendable
+{
+    final StringBuilder _buffer;
+
+    public Utf8StringBuilder()
+    {
+        super(new StringBuilder());
+        _buffer=(StringBuilder)_appendable;
+    }
+
+    public Utf8StringBuilder(int capacity)
+    {
+        super(new StringBuilder(capacity));
+        _buffer=(StringBuilder)_appendable;
+    }
+
+    @Override
+    public int length()
+    {
+        return _buffer.length();
+    }
+
+    @Override
+    public void reset()
+    {
+        super.reset();
+        _buffer.setLength(0);
+    }
+
+    public StringBuilder getStringBuilder()
+    {
+        checkState();
+        return _buffer;
+    }
+
+    @Override
+    public String toString()
+    {
+        checkState();
+        return _buffer.toString();
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/ajax/JSON.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,1640 @@
+//
+//  ========================================================================
+//  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.util.ajax;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.Loader;
+import org.eclipse.jetty.util.QuotedStringTokenizer;
+import org.eclipse.jetty.util.TypeUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * JSON Parser and Generator.
+ * <p />
+ * This class provides some static methods to convert POJOs to and from JSON
+ * notation. The mapping from JSON to java is:
+ *
+ * <pre>
+ *   object ==> Map
+ *   array  ==> Object[]
+ *   number ==> Double or Long
+ *   string ==> String
+ *   null   ==> null
+ *   bool   ==> Boolean
+ * </pre>
+
+ * The java to JSON mapping is:
+ *
+ * <pre>
+ *   String --> string
+ *   Number --> number
+ *   Map    --> object
+ *   List   --> array
+ *   Array  --> array
+ *   null   --> null
+ *   Boolean--> boolean
+ *   Object --> string (dubious!)
+ * </pre>
+ *
+ * The interface {@link JSON.Convertible} may be implemented by classes that
+ * wish to externalize and initialize specific fields to and from JSON objects.
+ * Only directed acyclic graphs of objects are supported.
+ * <p />
+ * The interface {@link JSON.Generator} may be implemented by classes that know
+ * how to render themselves as JSON and the {@link #toString(Object)} method
+ * will use {@link JSON.Generator#addJSON(Appendable)} to generate the JSON.
+ * The class {@link JSON.Literal} may be used to hold pre-generated JSON object.
+ * <p />
+ * The interface {@link JSON.Convertor} may be implemented to provide static
+ * converters for objects that may be registered with
+ * {@link #registerConvertor(Class, Convertor)}.
+ * These converters are looked up by class, interface and super class by
+ * {@link #getConvertor(Class)}.
+ * <p />
+ * If a JSON object has a "class" field, then a java class for that name is
+ * loaded and the method {@link #convertTo(Class,Map)} is used to find a
+ * {@link JSON.Convertor} for that class.
+ * <p />
+ * If a JSON object has a "x-class" field then a direct lookup for a
+ * {@link JSON.Convertor} for that class name is done (without loading the class).
+ */
+public class JSON
+{
+    static final Logger LOG = Log.getLogger(JSON.class);
+    public final static JSON DEFAULT = new JSON();
+
+    private Map<String, Convertor> _convertors = new ConcurrentHashMap<String, Convertor>();
+    private int _stringBufferSize = 1024;
+
+    public JSON()
+    {
+    }
+
+    /**
+     * @return the initial stringBuffer size to use when creating JSON strings
+     *         (default 1024)
+     */
+    public int getStringBufferSize()
+    {
+        return _stringBufferSize;
+    }
+
+    /**
+     * @param stringBufferSize
+     *            the initial stringBuffer size to use when creating JSON
+     *            strings (default 1024)
+     */
+    public void setStringBufferSize(int stringBufferSize)
+    {
+        _stringBufferSize = stringBufferSize;
+    }
+
+    /**
+     * Register a {@link Convertor} for a class or interface.
+     *
+     * @param forClass
+     *            The class or interface that the convertor applies to
+     * @param convertor
+     *            the convertor
+     */
+    public static void registerConvertor(Class forClass, Convertor convertor)
+    {
+        DEFAULT.addConvertor(forClass,convertor);
+    }
+
+    public static JSON getDefault()
+    {
+        return DEFAULT;
+    }
+
+    @Deprecated
+    public static void setDefault(JSON json)
+    {
+    }
+
+    public static String toString(Object object)
+    {
+        StringBuilder buffer = new StringBuilder(DEFAULT.getStringBufferSize());
+        DEFAULT.append(buffer,object);
+        return buffer.toString();
+    }
+
+    public static String toString(Map object)
+    {
+        StringBuilder buffer = new StringBuilder(DEFAULT.getStringBufferSize());
+        DEFAULT.appendMap(buffer,object);
+        return buffer.toString();
+    }
+
+    public static String toString(Object[] array)
+    {
+        StringBuilder buffer = new StringBuilder(DEFAULT.getStringBufferSize());
+        DEFAULT.appendArray(buffer,array);
+        return buffer.toString();
+    }
+
+    /**
+     * @param s
+     *            String containing JSON object or array.
+     * @return A Map, Object array or primitive array parsed from the JSON.
+     */
+    public static Object parse(String s)
+    {
+        return DEFAULT.parse(new StringSource(s),false);
+    }
+
+    /**
+     * @param s
+     *            String containing JSON object or array.
+     * @param stripOuterComment
+     *            If true, an outer comment around the JSON is ignored.
+     * @return A Map, Object array or primitive array parsed from the JSON.
+     */
+    public static Object parse(String s, boolean stripOuterComment)
+    {
+        return DEFAULT.parse(new StringSource(s),stripOuterComment);
+    }
+
+    /**
+     * @param in
+     *            Reader containing JSON object or array.
+     * @return A Map, Object array or primitive array parsed from the JSON.
+     */
+    public static Object parse(Reader in) throws IOException
+    {
+        return DEFAULT.parse(new ReaderSource(in),false);
+    }
+
+    /**
+     * @param in
+     *            Reader containing JSON object or array.
+     * @param stripOuterComment
+     *            If true, an outer comment around the JSON is ignored.
+     * @return A Map, Object array or primitive array parsed from the JSON.
+     */
+    public static Object parse(Reader in, boolean stripOuterComment) throws IOException
+    {
+        return DEFAULT.parse(new ReaderSource(in),stripOuterComment);
+    }
+
+    /**
+     * @deprecated use {@link #parse(Reader)}
+     * @param in
+     *            Reader containing JSON object or array.
+     * @return A Map, Object array or primitive array parsed from the JSON.
+     */
+    @Deprecated
+    public static Object parse(InputStream in) throws IOException
+    {
+        return DEFAULT.parse(new StringSource(IO.toString(in)),false);
+    }
+
+    /**
+     * @deprecated use {@link #parse(Reader, boolean)}
+     * @param in
+     *            Stream containing JSON object or array.
+     * @param stripOuterComment
+     *            If true, an outer comment around the JSON is ignored.
+     * @return A Map, Object array or primitive array parsed from the JSON.
+     */
+    @Deprecated
+    public static Object parse(InputStream in, boolean stripOuterComment) throws IOException
+    {
+        return DEFAULT.parse(new StringSource(IO.toString(in)),stripOuterComment);
+    }
+
+    /**
+     * Convert Object to JSON
+     *
+     * @param object
+     *            The object to convert
+     * @return The JSON String
+     */
+    public String toJSON(Object object)
+    {
+        StringBuilder buffer = new StringBuilder(getStringBufferSize());
+        append(buffer,object);
+        return buffer.toString();
+    }
+
+    /**
+     * Convert JSON to Object
+     *
+     * @param json
+     *            The json to convert
+     * @return The object
+     */
+    public Object fromJSON(String json)
+    {
+        Source source = new StringSource(json);
+        return parse(source);
+    }
+
+    @Deprecated
+    public void append(StringBuffer buffer, Object object)
+    {
+        append((Appendable)buffer,object);
+    }
+
+    /**
+     * Append object as JSON to string buffer.
+     *
+     * @param buffer
+     *            the buffer to append to
+     * @param object
+     *            the object to append
+     */
+    public void append(Appendable buffer, Object object)
+    {
+        try
+        {
+            if (object == null)
+            {
+                buffer.append("null");
+            }
+            // Most likely first
+            else if (object instanceof Map)
+            {
+                appendMap(buffer,(Map)object);
+            }
+            else if (object instanceof String)
+            {
+                appendString(buffer,(String)object);
+            }
+            else if (object instanceof Number)
+            {
+                appendNumber(buffer,(Number)object);
+            }
+            else if (object instanceof Boolean)
+            {
+                appendBoolean(buffer,(Boolean)object);
+            }
+            else if (object.getClass().isArray())
+            {
+                appendArray(buffer,object);
+            }
+            else if (object instanceof Character)
+            {
+                appendString(buffer,object.toString());
+            }
+            else if (object instanceof Convertible)
+            {
+                appendJSON(buffer,(Convertible)object);
+            }
+            else if (object instanceof Generator)
+            {
+                appendJSON(buffer,(Generator)object);
+            }
+            else
+            {
+                // Check Convertor before Collection to support JSONCollectionConvertor
+                Convertor convertor = getConvertor(object.getClass());
+                if (convertor != null)
+                {
+                    appendJSON(buffer,convertor,object);
+                }
+                else if (object instanceof Collection)
+                {
+                    appendArray(buffer,(Collection)object);
+                }
+                else
+                {
+                    appendString(buffer,object.toString());
+                }
+            }
+        }
+        catch (IOException e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Deprecated
+    public void appendNull(StringBuffer buffer)
+    {
+        appendNull((Appendable)buffer);
+    }
+
+    public void appendNull(Appendable buffer)
+    {
+        try
+        {
+            buffer.append("null");
+        }
+        catch (IOException e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Deprecated
+    public void appendJSON(final StringBuffer buffer, final Convertor convertor, final Object object)
+    {
+        appendJSON((Appendable)buffer,convertor,object);
+    }
+
+    public void appendJSON(final Appendable buffer, final Convertor convertor, final Object object)
+    {
+        appendJSON(buffer,new Convertible()
+        {
+            public void fromJSON(Map object)
+            {
+            }
+
+            public void toJSON(Output out)
+            {
+                convertor.toJSON(object,out);
+            }
+        });
+    }
+
+    @Deprecated
+    public void appendJSON(final StringBuffer buffer, Convertible converter)
+    {
+        appendJSON((Appendable)buffer,converter);
+    }
+
+    public void appendJSON(final Appendable buffer, Convertible converter)
+    {
+        ConvertableOutput out=new ConvertableOutput(buffer);
+        converter.toJSON(out);
+        out.complete();
+    }
+
+    @Deprecated
+    public void appendJSON(StringBuffer buffer, Generator generator)
+    {
+        generator.addJSON(buffer);
+    }
+
+    public void appendJSON(Appendable buffer, Generator generator)
+    {
+        generator.addJSON(buffer);
+    }
+
+    @Deprecated
+    public void appendMap(StringBuffer buffer, Map<?,?> map)
+    {
+        appendMap((Appendable)buffer,map);
+    }
+
+    public void appendMap(Appendable buffer, Map<?,?> map)
+    {
+        try
+        {
+            if (map == null)
+            {
+                appendNull(buffer);
+                return;
+            }
+
+            buffer.append('{');
+            Iterator<?> iter = map.entrySet().iterator();
+            while (iter.hasNext())
+            {
+                Map.Entry<?,?> entry = (Map.Entry<?,?>)iter.next();
+                QuotedStringTokenizer.quote(buffer,entry.getKey().toString());
+                buffer.append(':');
+                append(buffer,entry.getValue());
+                if (iter.hasNext())
+                    buffer.append(',');
+            }
+
+            buffer.append('}');
+        }
+        catch (IOException e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Deprecated
+    public void appendArray(StringBuffer buffer, Collection collection)
+    {
+        appendArray((Appendable)buffer,collection);
+    }
+
+    public void appendArray(Appendable buffer, Collection collection)
+    {
+        try
+        {
+            if (collection == null)
+            {
+                appendNull(buffer);
+                return;
+            }
+
+            buffer.append('[');
+            Iterator iter = collection.iterator();
+            boolean first = true;
+            while (iter.hasNext())
+            {
+                if (!first)
+                    buffer.append(',');
+
+                first = false;
+                append(buffer,iter.next());
+            }
+
+            buffer.append(']');
+        }
+        catch (IOException e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Deprecated
+    public void appendArray(StringBuffer buffer, Object array)
+    {
+    appendArray((Appendable)buffer,array);
+    }
+
+    public void appendArray(Appendable buffer, Object array)
+    {
+        try
+        {
+            if (array == null)
+            {
+                appendNull(buffer);
+                return;
+            }
+
+            buffer.append('[');
+            int length = Array.getLength(array);
+
+            for (int i = 0; i < length; i++)
+            {
+                if (i != 0)
+                    buffer.append(',');
+                append(buffer,Array.get(array,i));
+            }
+
+            buffer.append(']');
+        }
+        catch (IOException e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Deprecated
+    public void appendBoolean(StringBuffer buffer, Boolean b)
+    {
+        appendBoolean((Appendable)buffer,b);
+    }
+
+    public void appendBoolean(Appendable buffer, Boolean b)
+    {
+        try
+        {
+            if (b == null)
+            {
+                appendNull(buffer);
+                return;
+            }
+            buffer.append(b?"true":"false");
+        }
+        catch (IOException e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Deprecated
+    public void appendNumber(StringBuffer buffer, Number number)
+    {
+        appendNumber((Appendable)buffer,number);
+    }
+
+    public void appendNumber(Appendable buffer, Number number)
+    {
+        try
+        {
+            if (number == null)
+            {
+                appendNull(buffer);
+                return;
+            }
+            buffer.append(String.valueOf(number));
+        }
+        catch (IOException e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Deprecated
+    public void appendString(StringBuffer buffer, String string)
+    {
+        appendString((Appendable)buffer,string);
+    }
+
+    public void appendString(Appendable buffer, String string)
+    {
+        if (string == null)
+        {
+            appendNull(buffer);
+            return;
+        }
+
+        QuotedStringTokenizer.quote(buffer,string);
+    }
+
+    // Parsing utilities
+
+    protected String toString(char[] buffer, int offset, int length)
+    {
+        return new String(buffer,offset,length);
+    }
+
+    protected Map<String, Object> newMap()
+    {
+        return new HashMap<String, Object>();
+    }
+
+    protected Object[] newArray(int size)
+    {
+        return new Object[size];
+    }
+
+    protected JSON contextForArray()
+    {
+        return this;
+    }
+
+    protected JSON contextFor(String field)
+    {
+        return this;
+    }
+
+    protected Object convertTo(Class type, Map map)
+    {
+        if (type != null && Convertible.class.isAssignableFrom(type))
+        {
+            try
+            {
+                Convertible conv = (Convertible)type.newInstance();
+                conv.fromJSON(map);
+                return conv;
+            }
+            catch (Exception e)
+            {
+                throw new RuntimeException(e);
+            }
+        }
+
+        Convertor convertor = getConvertor(type);
+        if (convertor != null)
+        {
+            return convertor.fromJSON(map);
+        }
+        return map;
+    }
+
+    /**
+     * Register a {@link Convertor} for a class or interface.
+     *
+     * @param forClass
+     *            The class or interface that the convertor applies to
+     * @param convertor
+     *            the convertor
+     */
+    public void addConvertor(Class forClass, Convertor convertor)
+    {
+        _convertors.put(forClass.getName(),convertor);
+    }
+
+    /**
+     * Lookup a convertor for a class.
+     * <p>
+     * If no match is found for the class, then the interfaces for the class are
+     * tried. If still no match is found, then the super class and it's
+     * interfaces are tried recursively.
+     *
+     * @param forClass
+     *            The class
+     * @return a {@link JSON.Convertor} or null if none were found.
+     */
+    protected Convertor getConvertor(Class forClass)
+    {
+        Class cls = forClass;
+        Convertor convertor = _convertors.get(cls.getName());
+        if (convertor == null && this != DEFAULT)
+            convertor = DEFAULT.getConvertor(cls);
+
+        while (convertor == null && cls != Object.class)
+        {
+            Class[] ifs = cls.getInterfaces();
+            int i = 0;
+            while (convertor == null && ifs != null && i < ifs.length)
+                convertor = _convertors.get(ifs[i++].getName());
+            if (convertor == null)
+            {
+                cls = cls.getSuperclass();
+                convertor = _convertors.get(cls.getName());
+            }
+        }
+        return convertor;
+    }
+
+    /**
+     * Register a {@link JSON.Convertor} for a named class or interface.
+     *
+     * @param name
+     *            name of a class or an interface that the convertor applies to
+     * @param convertor
+     *            the convertor
+     */
+    public void addConvertorFor(String name, Convertor convertor)
+    {
+        _convertors.put(name,convertor);
+    }
+
+    /**
+     * Lookup a convertor for a named class.
+     *
+     * @param name
+     *            name of the class
+     * @return a {@link JSON.Convertor} or null if none were found.
+     */
+    public Convertor getConvertorFor(String name)
+    {
+        Convertor convertor = _convertors.get(name);
+        if (convertor == null && this != DEFAULT)
+            convertor = DEFAULT.getConvertorFor(name);
+        return convertor;
+    }
+
+    public Object parse(Source source, boolean stripOuterComment)
+    {
+        int comment_state = 0; // 0=no comment, 1="/", 2="/*", 3="/* *" -1="//"
+        if (!stripOuterComment)
+            return parse(source);
+
+        int strip_state = 1; // 0=no strip, 1=wait for /*, 2= wait for */
+
+        Object o = null;
+        while (source.hasNext())
+        {
+            char c = source.peek();
+
+            // handle // or /* comment
+            if (comment_state == 1)
+            {
+                switch (c)
+                {
+                    case '/':
+                        comment_state = -1;
+                        break;
+                    case '*':
+                        comment_state = 2;
+                        if (strip_state == 1)
+                        {
+                            comment_state = 0;
+                            strip_state = 2;
+                        }
+                }
+            }
+            // handle /* */ comment
+            else if (comment_state > 1)
+            {
+                switch (c)
+                {
+                    case '*':
+                        comment_state = 3;
+                        break;
+                    case '/':
+                        if (comment_state == 3)
+                        {
+                            comment_state = 0;
+                            if (strip_state == 2)
+                                return o;
+                        }
+                        else
+                            comment_state = 2;
+                        break;
+                    default:
+                        comment_state = 2;
+                }
+            }
+            // handle // comment
+            else if (comment_state < 0)
+            {
+                switch (c)
+                {
+                    case '\r':
+                    case '\n':
+                        comment_state = 0;
+                    default:
+                        break;
+                }
+            }
+            // handle unknown
+            else
+            {
+                if (!Character.isWhitespace(c))
+                {
+                    if (c == '/')
+                        comment_state = 1;
+                    else if (c == '*')
+                        comment_state = 3;
+                    else if (o == null)
+                    {
+                        o = parse(source);
+                        continue;
+                    }
+                }
+            }
+
+            source.next();
+        }
+
+        return o;
+    }
+
+    public Object parse(Source source)
+    {
+        int comment_state = 0; // 0=no comment, 1="/", 2="/*", 3="/* *" -1="//"
+
+        while (source.hasNext())
+        {
+            char c = source.peek();
+
+            // handle // or /* comment
+            if (comment_state == 1)
+            {
+                switch (c)
+                {
+                    case '/':
+                        comment_state = -1;
+                        break;
+                    case '*':
+                        comment_state = 2;
+                }
+            }
+            // handle /* */ comment
+            else if (comment_state > 1)
+            {
+                switch (c)
+                {
+                    case '*':
+                        comment_state = 3;
+                        break;
+                    case '/':
+                        if (comment_state == 3)
+                            comment_state = 0;
+                        else
+                            comment_state = 2;
+                        break;
+                    default:
+                        comment_state = 2;
+                }
+            }
+            // handle // comment
+            else if (comment_state < 0)
+            {
+                switch (c)
+                {
+                    case '\r':
+                    case '\n':
+                        comment_state = 0;
+                        break;
+                    default:
+                        break;
+                }
+            }
+            // handle unknown
+            else
+            {
+                switch (c)
+                {
+                    case '{':
+                        return parseObject(source);
+                    case '[':
+                        return parseArray(source);
+                    case '"':
+                        return parseString(source);
+                    case '-':
+                        return parseNumber(source);
+
+                    case 'n':
+                        complete("null",source);
+                        return null;
+                    case 't':
+                        complete("true",source);
+                        return Boolean.TRUE;
+                    case 'f':
+                        complete("false",source);
+                        return Boolean.FALSE;
+                    case 'u':
+                        complete("undefined",source);
+                        return null;
+                    case 'N':
+                        complete("NaN",source);
+                        return null;
+
+                    case '/':
+                        comment_state = 1;
+                        break;
+
+                    default:
+                        if (Character.isDigit(c))
+                            return parseNumber(source);
+                        else if (Character.isWhitespace(c))
+                            break;
+                        return handleUnknown(source,c);
+                }
+            }
+            source.next();
+        }
+
+        return null;
+    }
+
+    protected Object handleUnknown(Source source, char c)
+    {
+        throw new IllegalStateException("unknown char '" + c + "'(" + (int)c + ") in " + source);
+    }
+
+    protected Object parseObject(Source source)
+    {
+        if (source.next() != '{')
+            throw new IllegalStateException();
+        Map<String, Object> map = newMap();
+
+        char next = seekTo("\"}",source);
+
+        while (source.hasNext())
+        {
+            if (next == '}')
+            {
+                source.next();
+                break;
+            }
+
+            String name = parseString(source);
+            seekTo(':',source);
+            source.next();
+
+            Object value = contextFor(name).parse(source);
+            map.put(name,value);
+
+            seekTo(",}",source);
+            next = source.next();
+            if (next == '}')
+                break;
+            else
+                next = seekTo("\"}",source);
+        }
+
+        String xclassname = (String)map.get("x-class");
+        if (xclassname != null)
+        {
+            Convertor c = getConvertorFor(xclassname);
+            if (c != null)
+                return c.fromJSON(map);
+            LOG.warn("No Convertor for x-class '{}'", xclassname);
+        }
+
+        String classname = (String)map.get("class");
+        if (classname != null)
+        {
+            try
+            {
+                Class c = Loader.loadClass(JSON.class,classname);
+                return convertTo(c,map);
+            }
+            catch (ClassNotFoundException e)
+            {
+                LOG.warn("No Class for '{}'", classname);
+            }
+        }
+
+        return map;
+    }
+
+    protected Object parseArray(Source source)
+    {
+        if (source.next() != '[')
+            throw new IllegalStateException();
+
+        int size = 0;
+        ArrayList list = null;
+        Object item = null;
+        boolean coma = true;
+
+        while (source.hasNext())
+        {
+            char c = source.peek();
+            switch (c)
+            {
+                case ']':
+                    source.next();
+                    switch (size)
+                    {
+                        case 0:
+                            return newArray(0);
+                        case 1:
+                            Object array = newArray(1);
+                            Array.set(array,0,item);
+                            return array;
+                        default:
+                            return list.toArray(newArray(list.size()));
+                    }
+
+                case ',':
+                    if (coma)
+                        throw new IllegalStateException();
+                    coma = true;
+                    source.next();
+                    break;
+
+                default:
+                    if (Character.isWhitespace(c))
+                        source.next();
+                    else
+                    {
+                        coma = false;
+                        if (size++ == 0)
+                            item = contextForArray().parse(source);
+                        else if (list == null)
+                        {
+                            list = new ArrayList();
+                            list.add(item);
+                            item = contextForArray().parse(source);
+                            list.add(item);
+                            item = null;
+                        }
+                        else
+                        {
+                            item = contextForArray().parse(source);
+                            list.add(item);
+                            item = null;
+                        }
+                    }
+            }
+
+        }
+
+        throw new IllegalStateException("unexpected end of array");
+    }
+
+    protected String parseString(Source source)
+    {
+        if (source.next() != '"')
+            throw new IllegalStateException();
+
+        boolean escape = false;
+
+        StringBuilder b = null;
+        final char[] scratch = source.scratchBuffer();
+
+        if (scratch != null)
+        {
+            int i = 0;
+            while (source.hasNext())
+            {
+                if (i >= scratch.length)
+                {
+                    // we have filled the scratch buffer, so we must
+                    // use the StringBuffer for a large string
+                    b = new StringBuilder(scratch.length * 2);
+                    b.append(scratch,0,i);
+                    break;
+                }
+
+                char c = source.next();
+
+                if (escape)
+                {
+                    escape = false;
+                    switch (c)
+                    {
+                        case '"':
+                            scratch[i++] = '"';
+                            break;
+                        case '\\':
+                            scratch[i++] = '\\';
+                            break;
+                        case '/':
+                            scratch[i++] = '/';
+                            break;
+                        case 'b':
+                            scratch[i++] = '\b';
+                            break;
+                        case 'f':
+                            scratch[i++] = '\f';
+                            break;
+                        case 'n':
+                            scratch[i++] = '\n';
+                            break;
+                        case 'r':
+                            scratch[i++] = '\r';
+                            break;
+                        case 't':
+                            scratch[i++] = '\t';
+                            break;
+                        case 'u':
+                            char uc = (char)((TypeUtil.convertHexDigit((byte)source.next()) << 12) + (TypeUtil.convertHexDigit((byte)source.next()) << 8)
+                                    + (TypeUtil.convertHexDigit((byte)source.next()) << 4) + (TypeUtil.convertHexDigit((byte)source.next())));
+                            scratch[i++] = uc;
+                            break;
+                        default:
+                            scratch[i++] = c;
+                    }
+                }
+                else if (c == '\\')
+                {
+                    escape = true;
+                }
+                else if (c == '\"')
+                {
+                    // Return string that fits within scratch buffer
+                    return toString(scratch,0,i);
+                }
+                else
+                {
+                    scratch[i++] = c;
+                }
+            }
+
+            // Missing end quote, but return string anyway ?
+            if (b == null)
+                return toString(scratch,0,i);
+        }
+        else
+            b = new StringBuilder(getStringBufferSize());
+
+        // parse large string into string buffer
+        final StringBuilder builder=b;
+        while (source.hasNext())
+        {
+            char c = source.next();
+
+            if (escape)
+            {
+                escape = false;
+                switch (c)
+                {
+                    case '"':
+                        builder.append('"');
+                        break;
+                    case '\\':
+                        builder.append('\\');
+                        break;
+                    case '/':
+                        builder.append('/');
+                        break;
+                    case 'b':
+                        builder.append('\b');
+                        break;
+                    case 'f':
+                        builder.append('\f');
+                        break;
+                    case 'n':
+                        builder.append('\n');
+                        break;
+                    case 'r':
+                        builder.append('\r');
+                        break;
+                    case 't':
+                        builder.append('\t');
+                        break;
+                    case 'u':
+                        char uc = (char)((TypeUtil.convertHexDigit((byte)source.next()) << 12) + (TypeUtil.convertHexDigit((byte)source.next()) << 8)
+                                + (TypeUtil.convertHexDigit((byte)source.next()) << 4) + (TypeUtil.convertHexDigit((byte)source.next())));
+                        builder.append(uc);
+                        break;
+                    default:
+                        builder.append(c);
+                }
+            }
+            else if (c == '\\')
+            {
+                escape = true;
+            }
+            else if (c == '\"')
+            {
+                break;
+            }
+            else
+            {
+                builder.append(c);
+            }
+        }
+        return builder.toString();
+    }
+
+    public Number parseNumber(Source source)
+    {
+        boolean minus = false;
+        long number = 0;
+        StringBuilder buffer = null;
+
+        longLoop: while (source.hasNext())
+        {
+            char c = source.peek();
+            switch (c)
+            {
+                case '0':
+                case '1':
+                case '2':
+                case '3':
+                case '4':
+                case '5':
+                case '6':
+                case '7':
+                case '8':
+                case '9':
+                    number = number * 10 + (c - '0');
+                    source.next();
+                    break;
+
+                case '-':
+                case '+':
+                    if (number != 0)
+                        throw new IllegalStateException("bad number");
+                    minus = true;
+                    source.next();
+                    break;
+
+                case '.':
+                case 'e':
+                case 'E':
+                    buffer = new StringBuilder(16);
+                    if (minus)
+                        buffer.append('-');
+                    buffer.append(number);
+                    buffer.append(c);
+                    source.next();
+                    break longLoop;
+
+                default:
+                    break longLoop;
+            }
+        }
+
+        if (buffer == null)
+            return minus ? -1 * number : number;
+
+        doubleLoop: while (source.hasNext())
+        {
+            char c = source.peek();
+            switch (c)
+            {
+                case '0':
+                case '1':
+                case '2':
+                case '3':
+                case '4':
+                case '5':
+                case '6':
+                case '7':
+                case '8':
+                case '9':
+                case '-':
+                case '.':
+                case '+':
+                case 'e':
+                case 'E':
+                    buffer.append(c);
+                    source.next();
+                    break;
+
+                default:
+                    break doubleLoop;
+            }
+        }
+        return new Double(buffer.toString());
+
+    }
+
+    protected void seekTo(char seek, Source source)
+    {
+        while (source.hasNext())
+        {
+            char c = source.peek();
+            if (c == seek)
+                return;
+
+            if (!Character.isWhitespace(c))
+                throw new IllegalStateException("Unexpected '" + c + " while seeking '" + seek + "'");
+            source.next();
+        }
+
+        throw new IllegalStateException("Expected '" + seek + "'");
+    }
+
+    protected char seekTo(String seek, Source source)
+    {
+        while (source.hasNext())
+        {
+            char c = source.peek();
+            if (seek.indexOf(c) >= 0)
+            {
+                return c;
+            }
+
+            if (!Character.isWhitespace(c))
+                throw new IllegalStateException("Unexpected '" + c + "' while seeking one of '" + seek + "'");
+            source.next();
+        }
+
+        throw new IllegalStateException("Expected one of '" + seek + "'");
+    }
+
+    protected static void complete(String seek, Source source)
+    {
+        int i = 0;
+        while (source.hasNext() && i < seek.length())
+        {
+            char c = source.next();
+            if (c != seek.charAt(i++))
+                throw new IllegalStateException("Unexpected '" + c + " while seeking  \"" + seek + "\"");
+        }
+
+        if (i < seek.length())
+            throw new IllegalStateException("Expected \"" + seek + "\"");
+    }
+
+    private final class ConvertableOutput implements Output
+    {
+        private final Appendable _buffer;
+        char c = '{';
+
+        private ConvertableOutput(Appendable buffer)
+        {
+            _buffer = buffer;
+        }
+
+        public void complete()
+        {
+            try
+            {
+                if (c == '{')
+                    _buffer.append("{}");
+                else if (c != 0)
+                    _buffer.append("}");
+            }
+            catch (IOException e)
+            {
+                throw new RuntimeException(e);
+            }
+        }
+
+        public void add(Object obj)
+        {
+            if (c == 0)
+                throw new IllegalStateException();
+            append(_buffer,obj);
+            c = 0;
+        }
+
+        public void addClass(Class type)
+        {
+            try
+            {
+                if (c == 0)
+                    throw new IllegalStateException();
+                _buffer.append(c);
+                _buffer.append("\"class\":");
+                append(_buffer,type.getName());
+                c = ',';
+            }
+            catch (IOException e)
+            {
+                throw new RuntimeException(e);
+            }
+        }
+
+        public void add(String name, Object value)
+        {
+            try
+            {
+                if (c == 0)
+                    throw new IllegalStateException();
+                _buffer.append(c);
+                QuotedStringTokenizer.quote(_buffer,name);
+                _buffer.append(':');
+                append(_buffer,value);
+                c = ',';
+            }
+            catch (IOException e)
+            {
+                throw new RuntimeException(e);
+            }
+        }
+
+        public void add(String name, double value)
+        {
+            try
+            {
+                if (c == 0)
+                    throw new IllegalStateException();
+                _buffer.append(c);
+                QuotedStringTokenizer.quote(_buffer,name);
+                _buffer.append(':');
+                appendNumber(_buffer, value);
+                c = ',';
+            }
+            catch (IOException e)
+            {
+                throw new RuntimeException(e);
+            }
+        }
+
+        public void add(String name, long value)
+        {
+            try
+            {
+                if (c == 0)
+                    throw new IllegalStateException();
+                _buffer.append(c);
+                QuotedStringTokenizer.quote(_buffer,name);
+                _buffer.append(':');
+                appendNumber(_buffer, value);
+                c = ',';
+            }
+            catch (IOException e)
+            {
+                throw new RuntimeException(e);
+            }
+        }
+
+        public void add(String name, boolean value)
+        {
+            try
+            {
+                if (c == 0)
+                    throw new IllegalStateException();
+                _buffer.append(c);
+                QuotedStringTokenizer.quote(_buffer,name);
+                _buffer.append(':');
+                appendBoolean(_buffer,value?Boolean.TRUE:Boolean.FALSE);
+                c = ',';
+            }
+            catch (IOException e)
+            {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    public interface Source
+    {
+        boolean hasNext();
+
+        char next();
+
+        char peek();
+
+        char[] scratchBuffer();
+    }
+
+    public static class StringSource implements Source
+    {
+        private final String string;
+        private int index;
+        private char[] scratch;
+
+        public StringSource(String s)
+        {
+            string = s;
+        }
+
+        public boolean hasNext()
+        {
+            if (index < string.length())
+                return true;
+            scratch = null;
+            return false;
+        }
+
+        public char next()
+        {
+            return string.charAt(index++);
+        }
+
+        public char peek()
+        {
+            return string.charAt(index);
+        }
+
+        @Override
+        public String toString()
+        {
+            return string.substring(0,index) + "|||" + string.substring(index);
+        }
+
+        public char[] scratchBuffer()
+        {
+            if (scratch == null)
+                scratch = new char[string.length()];
+            return scratch;
+        }
+    }
+
+    public static class ReaderSource implements Source
+    {
+        private Reader _reader;
+        private int _next = -1;
+        private char[] scratch;
+
+        public ReaderSource(Reader r)
+        {
+            _reader = r;
+        }
+
+        public void setReader(Reader reader)
+        {
+            _reader = reader;
+            _next = -1;
+        }
+
+        public boolean hasNext()
+        {
+            getNext();
+            if (_next < 0)
+            {
+                scratch = null;
+                return false;
+            }
+            return true;
+        }
+
+        public char next()
+        {
+            getNext();
+            char c = (char)_next;
+            _next = -1;
+            return c;
+        }
+
+        public char peek()
+        {
+            getNext();
+            return (char)_next;
+        }
+
+        private void getNext()
+        {
+            if (_next < 0)
+            {
+                try
+                {
+                    _next = _reader.read();
+                }
+                catch (IOException e)
+                {
+                    throw new RuntimeException(e);
+                }
+            }
+        }
+
+        public char[] scratchBuffer()
+        {
+            if (scratch == null)
+                scratch = new char[1024];
+            return scratch;
+        }
+
+    }
+
+    /**
+     * JSON Output class for use by {@link Convertible}.
+     */
+    public interface Output
+    {
+        public void addClass(Class c);
+
+        public void add(Object obj);
+
+        public void add(String name, Object value);
+
+        public void add(String name, double value);
+
+        public void add(String name, long value);
+
+        public void add(String name, boolean value);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * JSON Convertible object. Object can implement this interface in a similar
+     * way to the {@link Externalizable} interface is used to allow classes to
+     * provide their own serialization mechanism.
+     * <p>
+     * A JSON.Convertible object may be written to a JSONObject or initialized
+     * from a Map of field names to values.
+     * <p>
+     * If the JSON is to be convertible back to an Object, then the method
+     * {@link Output#addClass(Class)} must be called from within toJSON()
+     *
+     */
+    public interface Convertible
+    {
+        public void toJSON(Output out);
+
+        public void fromJSON(Map object);
+    }
+
+    /**
+     * Static JSON Convertor.
+     * <p>
+     * may be implemented to provide static convertors for objects that may be
+     * registered with
+     * {@link JSON#registerConvertor(Class, org.eclipse.jetty.util.ajax.JSON.Convertor)}
+     * . These convertors are looked up by class, interface and super class by
+     * {@link JSON#getConvertor(Class)}. Convertors should be used when the
+     * classes to be converted cannot implement {@link Convertible} or
+     * {@link Generator}.
+     */
+    public interface Convertor
+    {
+        public void toJSON(Object obj, Output out);
+
+        public Object fromJSON(Map object);
+    }
+
+    /**
+     * JSON Generator. A class that can add it's JSON representation directly to
+     * a StringBuffer. This is useful for object instances that are frequently
+     * converted and wish to avoid multiple Conversions
+     */
+    public interface Generator
+    {
+        public void addJSON(Appendable buffer);
+    }
+
+    /**
+     * A Literal JSON generator A utility instance of {@link JSON.Generator}
+     * that holds a pre-generated string on JSON text.
+     */
+    public static class Literal implements Generator
+    {
+        private String _json;
+
+        /**
+         * Construct a literal JSON instance for use by
+         * {@link JSON#toString(Object)}. If {@link Log#isDebugEnabled()} is
+         * true, the JSON will be parsed to check validity
+         *
+         * @param json
+         *            A literal JSON string.
+         */
+        public Literal(String json)
+        {
+            if (LOG.isDebugEnabled()) // TODO: Make this a configurable option on JSON instead!
+                parse(json);
+            _json = json;
+        }
+
+        @Override
+        public String toString()
+        {
+            return _json;
+        }
+
+        public void addJSON(Appendable buffer)
+        {
+            try
+            {
+                buffer.append(_json);
+            }
+            catch(IOException e)
+            {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/ajax/JSONCollectionConvertor.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,50 @@
+//
+//  ========================================================================
+//  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.util.ajax;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+
+import org.eclipse.jetty.util.Loader;
+
+public class JSONCollectionConvertor implements JSON.Convertor
+{
+    public void toJSON(Object obj, JSON.Output out)
+    {
+        out.addClass(obj.getClass());
+        out.add("list", ((Collection)obj).toArray());
+    }
+
+    public Object fromJSON(Map object)
+    {
+        try
+        {
+            Collection result = (Collection)Loader.loadClass(getClass(), (String)object.get("class")).newInstance();
+            Collections.addAll(result, (Object[])object.get("list"));
+            return result;
+        }
+        catch (Exception x)
+        {
+            if (x instanceof RuntimeException)
+                throw (RuntimeException)x;
+            throw new RuntimeException(x);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/ajax/JSONDateConvertor.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,107 @@
+//
+//  ========================================================================
+//  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.util.ajax;
+
+import java.text.DateFormatSymbols;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+
+import org.eclipse.jetty.util.DateCache;
+import org.eclipse.jetty.util.ajax.JSON.Output;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/* ------------------------------------------------------------ */
+/**
+* Convert a {@link Date} to JSON.
+* If fromJSON is true in the constructor, the JSON generated will
+* be of the form {class="java.util.Date",value="1/1/1970 12:00 GMT"}
+* If fromJSON is false, then only the string value of the date is generated.
+*/
+public class JSONDateConvertor implements JSON.Convertor
+{
+    private static final Logger LOG = Log.getLogger(JSONDateConvertor.class);
+
+    private final boolean _fromJSON;
+    private final DateCache _dateCache;
+    private final SimpleDateFormat _format;
+
+    public JSONDateConvertor()
+    {
+        this(false);
+    }
+
+    public JSONDateConvertor(boolean fromJSON)
+    {
+        this(DateCache.DEFAULT_FORMAT,TimeZone.getTimeZone("GMT"),fromJSON);
+    }
+
+    public JSONDateConvertor(String format,TimeZone zone,boolean fromJSON)
+    {
+        _dateCache=new DateCache(format);
+        _dateCache.setTimeZone(zone);
+        _fromJSON=fromJSON;
+        _format=new SimpleDateFormat(format);
+        _format.setTimeZone(zone);
+    }
+
+    public JSONDateConvertor(String format, TimeZone zone, boolean fromJSON, Locale locale)
+    {
+        _dateCache = new DateCache(format, locale);
+        _dateCache.setTimeZone(zone);
+        _fromJSON = fromJSON;
+        _format = new SimpleDateFormat(format, new DateFormatSymbols(locale));
+        _format.setTimeZone(zone);
+    }
+
+    public Object fromJSON(Map map)
+    {
+        if (!_fromJSON)
+            throw new UnsupportedOperationException();
+        try
+        {
+            synchronized(_format)
+            {
+                return _format.parseObject((String)map.get("value"));
+            }
+        }
+        catch(Exception e)
+        {
+            LOG.warn(e);
+        }
+        return null;
+    }
+
+    public void toJSON(Object obj, Output out)
+    {
+        String date = _dateCache.format((Date)obj);
+        if (_fromJSON)
+        {
+            out.addClass(obj.getClass());
+            out.add("value",date);
+        }
+        else
+        {
+            out.add(date);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/ajax/JSONEnumConvertor.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,93 @@
+//
+//  ========================================================================
+//  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.util.ajax;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+
+import org.eclipse.jetty.util.Loader;
+import org.eclipse.jetty.util.ajax.JSON.Output;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/* ------------------------------------------------------------ */
+/**
+ * Convert an {@link Enum} to JSON.
+ * If fromJSON is true in the constructor, the JSON generated will
+ * be of the form {class="com.acme.TrafficLight",value="Green"}
+ * If fromJSON is false, then only the string value of the enum is generated.
+ *
+ *
+ */
+public class JSONEnumConvertor implements JSON.Convertor
+{
+    private static final Logger LOG = Log.getLogger(JSONEnumConvertor.class);
+    private boolean _fromJSON;
+    private Method _valueOf;
+    {
+        try
+        {
+            Class<?> e = Loader.loadClass(getClass(),"java.lang.Enum");
+            _valueOf=e.getMethod("valueOf",Class.class,String.class);
+        }
+        catch(Exception e)
+        {
+            throw new RuntimeException("!Enums",e);
+        }
+    }
+
+    public JSONEnumConvertor()
+    {
+        this(false);
+    }
+
+    public JSONEnumConvertor(boolean fromJSON)
+    {
+        _fromJSON=fromJSON;
+    }
+
+    public Object fromJSON(Map map)
+    {
+        if (!_fromJSON)
+            throw new UnsupportedOperationException();
+        try
+        {
+            Class c=Loader.loadClass(getClass(),(String)map.get("class"));
+            return _valueOf.invoke(null,c,map.get("value"));
+        }
+        catch(Exception e)
+        {
+            LOG.warn(e);
+        }
+        return null;
+    }
+
+    public void toJSON(Object obj, Output out)
+    {
+        if (_fromJSON)
+        {
+            out.addClass(obj.getClass());
+            out.add("value",((Enum)obj).name());
+        }
+        else
+        {
+            out.add(((Enum)obj).name());
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/ajax/JSONObjectConvertor.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,115 @@
+//
+//  ========================================================================
+//  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.util.ajax;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.jetty.util.ajax.JSON.Output;
+
+/* ------------------------------------------------------------ */
+/**
+ * Convert an Object to JSON using reflection on getters methods.
+ * 
+ * 
+ *
+ */
+public class JSONObjectConvertor implements JSON.Convertor
+{
+    private boolean _fromJSON;
+    private Set _excluded=null;
+
+    public JSONObjectConvertor()
+    {
+        _fromJSON=false;
+    }
+    
+    public JSONObjectConvertor(boolean fromJSON)
+    {
+        _fromJSON=fromJSON;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @param fromJSON
+     * @param excluded An array of field names to exclude from the conversion
+     */
+    public JSONObjectConvertor(boolean fromJSON,String[] excluded)
+    {
+        _fromJSON=fromJSON;
+        if (excluded!=null)
+            _excluded=new HashSet(Arrays.asList(excluded));
+    }
+
+    public Object fromJSON(Map map)
+    {
+        if (_fromJSON)
+            throw new UnsupportedOperationException();
+        return map;
+    }
+
+    public void toJSON(Object obj, Output out)
+    {
+        try
+        {
+            Class c=obj.getClass();
+
+            if (_fromJSON)
+                out.addClass(obj.getClass());
+
+            Method[] methods = obj.getClass().getMethods();
+
+            for (int i=0;i<methods.length;i++)
+            {
+                Method m=methods[i];
+                if (!Modifier.isStatic(m.getModifiers()) &&  
+                        m.getParameterTypes().length==0 && 
+                        m.getReturnType()!=null &&
+                        m.getDeclaringClass()!=Object.class)
+                {
+                    String name=m.getName();
+                    if (name.startsWith("is"))
+                        name=name.substring(2,3).toLowerCase(Locale.ENGLISH)+name.substring(3);
+                    else if (name.startsWith("get"))
+                        name=name.substring(3,4).toLowerCase(Locale.ENGLISH)+name.substring(4);
+                    else
+                        continue;
+
+                    if (includeField(name,obj,m))
+                        out.add(name, m.invoke(obj,(Object[])null));
+                }
+            }
+        } 
+        catch (Throwable e)
+        {
+            throw new IllegalArgumentException(e);
+        }
+    }
+    
+    protected boolean includeField(String name, Object o, Method m)
+    {
+        return _excluded==null || !_excluded.contains(name);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/ajax/JSONPojoConvertor.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,431 @@
+//
+//  ========================================================================
+//  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.util.ajax;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.jetty.util.ajax.JSON.Output;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+/**
+ * Converts POJOs to JSON and vice versa.
+ * The key difference:
+ *  - returns the actual object from Convertor.fromJSON (JSONObjectConverter returns a Map)
+ *  - the getters/setters are resolved at initialization (JSONObjectConverter resolves it at runtime)
+ *  - correctly sets the number fields
+ * 
+ */
+public class JSONPojoConvertor implements JSON.Convertor
+{
+    private static final Logger LOG = Log.getLogger(JSONPojoConvertor.class);
+    public static final Object[] GETTER_ARG = new Object[]{}, NULL_ARG = new Object[]{null};
+    private static final Map<Class<?>, NumberType> __numberTypes = new HashMap<Class<?>, NumberType>();
+    
+    public static NumberType getNumberType(Class<?> clazz)
+    {
+        return __numberTypes.get(clazz);
+    }
+    
+    protected boolean _fromJSON;
+    protected Class<?> _pojoClass;
+    protected Map<String,Method> _getters = new HashMap<String,Method>();
+    protected Map<String,Setter> _setters = new HashMap<String,Setter>();
+    protected Set<String> _excluded;
+
+    /**
+     * @param pojoClass The class to convert
+     */
+    public JSONPojoConvertor(Class<?> pojoClass)
+    {
+        this(pojoClass, (Set<String>)null, true);
+    }
+
+    /**
+     * @param pojoClass The class to convert
+     * @param excluded The fields to exclude
+     */
+    public JSONPojoConvertor(Class<?> pojoClass, String[] excluded)
+    {
+        this(pojoClass, new HashSet<String>(Arrays.asList(excluded)), true);
+    }
+
+    /**
+     * @param pojoClass The class to convert
+     * @param excluded The fields to exclude
+     */
+    public JSONPojoConvertor(Class<?> pojoClass, Set<String> excluded)
+    {
+        this(pojoClass, excluded, true);
+    }
+
+    /**
+     * @param pojoClass The class to convert
+     * @param excluded The fields to exclude
+     * @param fromJSON If true, add a class field to the JSON
+     */
+    public JSONPojoConvertor(Class<?> pojoClass, Set<String> excluded, boolean fromJSON)
+    {
+        _pojoClass = pojoClass;
+        _excluded = excluded;
+        _fromJSON = fromJSON;
+        init();
+    }    
+
+    /**
+     * @param pojoClass The class to convert
+     * @param fromJSON If true, add a class field to the JSON
+     */
+    public JSONPojoConvertor(Class<?> pojoClass, boolean fromJSON)
+    {
+        this(pojoClass, (Set<String>)null, fromJSON);
+    }
+    
+    /* ------------------------------------------------------------ */
+    protected void init()
+    {
+        Method[] methods = _pojoClass.getMethods();
+        for (int i=0;i<methods.length;i++)
+        {
+            Method m=methods[i];
+            if (!Modifier.isStatic(m.getModifiers()) && m.getDeclaringClass()!=Object.class)
+            {
+                String name=m.getName();
+                switch(m.getParameterTypes().length)
+                {
+                    case 0:
+                        
+                        if(m.getReturnType()!=null)
+                        {
+                            if (name.startsWith("is") && name.length()>2)
+                                name=name.substring(2,3).toLowerCase(Locale.ENGLISH)+name.substring(3);
+                            else if (name.startsWith("get") && name.length()>3)
+                                name=name.substring(3,4).toLowerCase(Locale.ENGLISH)+name.substring(4);
+                            else 
+                                break;
+                            if(includeField(name, m))
+                                addGetter(name, m);
+                        }
+                        break;
+                    case 1:
+                        if (name.startsWith("set") && name.length()>3)
+                        {
+                            name=name.substring(3,4).toLowerCase(Locale.ENGLISH)+name.substring(4);
+                            if(includeField(name, m))
+                                addSetter(name, m);
+                        }
+                        break;                
+                }
+            }
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    protected void addGetter(String name, Method method)
+    {
+        _getters.put(name, method);
+    }
+    
+    /* ------------------------------------------------------------ */
+    protected void addSetter(String name, Method method)
+    {
+        _setters.put(name, new Setter(name, method));
+    }
+    
+    /* ------------------------------------------------------------ */
+    protected Setter getSetter(String name)
+    {
+        return _setters.get(name);
+    }
+
+    /* ------------------------------------------------------------ */
+    protected boolean includeField(String name, Method m)
+    {
+        return _excluded==null || !_excluded.contains(name);
+    }
+    
+    /* ------------------------------------------------------------ */
+    protected int getExcludedCount()
+    {
+        return _excluded==null ? 0 : _excluded.size();
+    }
+
+    /* ------------------------------------------------------------ */
+    public Object fromJSON(Map object)
+    {        
+        Object obj = null;
+        try
+        {
+            obj = _pojoClass.newInstance();
+        }
+        catch(Exception e)
+        {
+            // TODO return Map instead?
+            throw new RuntimeException(e);
+        }
+        
+        setProps(obj, object);
+        return obj;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public int setProps(Object obj, Map<?,?> props)
+    {
+        int count = 0;
+        for(Iterator<?> iterator = props.entrySet().iterator(); iterator.hasNext();)
+        {
+            Map.Entry<?, ?> entry = (Map.Entry<?,?>) iterator.next();
+            Setter setter = getSetter((String)entry.getKey());
+            if(setter!=null)
+            {
+                try
+                {
+                    setter.invoke(obj, entry.getValue());                    
+                    count++;
+                }
+                catch(Exception e)
+                {
+                    // TODO throw exception?
+                    LOG.warn(_pojoClass.getName()+"#"+setter.getPropertyName()+" not set from "+
+                            (entry.getValue().getClass().getName())+"="+entry.getValue().toString());
+                    log(e);
+                }
+            }
+        }
+        return count;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void toJSON(Object obj, Output out)
+    {
+        if(_fromJSON)
+            out.addClass(_pojoClass);
+        for(Map.Entry<String,Method> entry : _getters.entrySet())
+        {            
+            try
+            {
+                out.add(entry.getKey(), entry.getValue().invoke(obj, GETTER_ARG));                    
+            }
+            catch(Exception e)
+            {
+                // TODO throw exception?
+                LOG.warn("{} property '{}' excluded. (errors)", _pojoClass.getName(), 
+                        entry.getKey());
+                log(e);
+            }
+        }        
+    }
+    
+    /* ------------------------------------------------------------ */
+    protected void log(Throwable t)
+    {
+        LOG.ignore(t);
+    }
+
+    /* ------------------------------------------------------------ */
+    public static class Setter
+    {
+        protected String _propertyName;
+        protected Method _setter;
+        protected NumberType _numberType;
+        protected Class<?> _type;
+        protected Class<?> _componentType;
+        
+        public Setter(String propertyName, Method method)
+        {
+            _propertyName = propertyName;
+            _setter = method;
+            _type = method.getParameterTypes()[0];
+            _numberType = __numberTypes.get(_type);
+            if(_numberType==null && _type.isArray())
+            {
+                _componentType = _type.getComponentType();
+                _numberType = __numberTypes.get(_componentType);
+            }
+        }
+        
+        public String getPropertyName()
+        {
+            return _propertyName;
+        }
+        
+        public Method getMethod()
+        {
+            return _setter;
+        }
+        
+        public NumberType getNumberType()
+        {
+            return _numberType;
+        }
+        
+        public Class<?> getType()
+        {
+            return _type;
+        }
+        
+        public Class<?> getComponentType()
+        {
+            return _componentType;
+        }
+        
+        public boolean isPropertyNumber()
+        {
+            return _numberType!=null;
+        }
+        
+        public void invoke(Object obj, Object value) throws IllegalArgumentException, 
+        IllegalAccessException, InvocationTargetException
+        {
+            if(value==null)
+                _setter.invoke(obj, NULL_ARG);
+            else
+                invokeObject(obj, value);
+        }
+        
+        protected void invokeObject(Object obj, Object value) throws IllegalArgumentException, 
+            IllegalAccessException, InvocationTargetException
+        {
+            
+            if (_type.isEnum())
+            {
+                if (value instanceof Enum)
+                    _setter.invoke(obj, new Object[]{value});
+                else
+                    _setter.invoke(obj, new Object[]{Enum.valueOf((Class<? extends Enum>)_type,value.toString())});
+            }
+            else if(_numberType!=null && value instanceof Number)
+            {
+                _setter.invoke(obj, new Object[]{_numberType.getActualValue((Number)value)});
+            }
+            else if (Character.TYPE.equals(_type) || Character.class.equals(_type))
+            {
+                _setter.invoke(obj, new Object[]{String.valueOf(value).charAt(0)});
+            }
+            else if(_componentType!=null && value.getClass().isArray())
+            {
+                if(_numberType==null)
+                {
+                    int len = Array.getLength(value);
+                    Object array = Array.newInstance(_componentType, len);
+                    try
+                    {
+                        System.arraycopy(value, 0, array, 0, len);
+                    }
+                    catch(Exception e)
+                    {                        
+                        // unusual array with multiple types
+                        LOG.ignore(e);
+                        _setter.invoke(obj, new Object[]{value});
+                        return;
+                    }                    
+                    _setter.invoke(obj, new Object[]{array});
+                }
+                else
+                {
+                    Object[] old = (Object[])value;
+                    Object array = Array.newInstance(_componentType, old.length);
+                    try
+                    {
+                        for(int i=0; i<old.length; i++)
+                            Array.set(array, i, _numberType.getActualValue((Number)old[i]));
+                    }
+                    catch(Exception e)
+                    {                        
+                        // unusual array with multiple types
+                        LOG.ignore(e);
+                        _setter.invoke(obj, new Object[]{value});
+                        return;
+                    }
+                    _setter.invoke(obj, new Object[]{array});
+                }
+            }
+            else
+                _setter.invoke(obj, new Object[]{value});
+        }
+    }
+    
+    public interface NumberType
+    {        
+        public Object getActualValue(Number number);     
+    }
+    
+    public static final NumberType SHORT = new NumberType()
+    {
+        public Object getActualValue(Number number)
+        {            
+            return new Short(number.shortValue());
+        } 
+    };
+
+    public static final NumberType INTEGER = new NumberType()
+    {
+        public Object getActualValue(Number number)
+        {            
+            return new Integer(number.intValue());
+        }
+    };
+    
+    public static final NumberType FLOAT = new NumberType()
+    {
+        public Object getActualValue(Number number)
+        {            
+            return new Float(number.floatValue());
+        }      
+    };
+
+    public static final NumberType LONG = new NumberType()
+    {
+        public Object getActualValue(Number number)
+        {            
+            return number instanceof Long ? number : new Long(number.longValue());
+        }     
+    };
+
+    public static final NumberType DOUBLE = new NumberType()
+    {
+        public Object getActualValue(Number number)
+        {            
+            return number instanceof Double ? number : new Double(number.doubleValue());
+        }       
+    };
+
+    static
+    {
+        __numberTypes.put(Short.class, SHORT);
+        __numberTypes.put(Short.TYPE, SHORT);
+        __numberTypes.put(Integer.class, INTEGER);
+        __numberTypes.put(Integer.TYPE, INTEGER);
+        __numberTypes.put(Long.class, LONG);
+        __numberTypes.put(Long.TYPE, LONG);
+        __numberTypes.put(Float.class, FLOAT);
+        __numberTypes.put(Float.TYPE, FLOAT);
+        __numberTypes.put(Double.class, DOUBLE);
+        __numberTypes.put(Double.TYPE, DOUBLE);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/ajax/JSONPojoConvertorFactory.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,110 @@
+//
+//  ========================================================================
+//  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.util.ajax;
+
+import java.util.Map;
+
+import org.eclipse.jetty.util.Loader;
+import org.eclipse.jetty.util.ajax.JSON.Convertor;
+import org.eclipse.jetty.util.ajax.JSON.Output;
+
+public class JSONPojoConvertorFactory implements JSON.Convertor
+{
+    private final JSON _json;
+    private final boolean _fromJson;
+
+    public JSONPojoConvertorFactory(JSON json)
+    {
+        if (json==null)
+        {
+            throw new IllegalArgumentException();
+        }
+        _json=json;
+        _fromJson=true;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @param json The JSON instance to use
+     * @param fromJSON If true, the class name of the objects is included
+     * in the generated JSON and is used to instantiate the object when
+     * JSON is parsed (otherwise a Map is used).
+     */
+    public JSONPojoConvertorFactory(JSON json,boolean fromJSON)
+    {
+        if (json==null)
+        {
+            throw new IllegalArgumentException();
+        }
+        _json=json;
+        _fromJson=fromJSON;
+    }
+    
+    /* ------------------------------------------------------------ */
+    public void toJSON(Object obj, Output out)
+    {
+        String clsName=obj.getClass().getName();
+        Convertor convertor=_json.getConvertorFor(clsName);
+        if (convertor==null)
+        {
+            try
+            {
+                Class cls=Loader.loadClass(JSON.class,clsName);
+                convertor=new JSONPojoConvertor(cls,_fromJson);
+                _json.addConvertorFor(clsName, convertor);
+             }
+            catch (ClassNotFoundException e)
+            {
+                JSON.LOG.warn(e);
+            }
+        }
+        if (convertor!=null)
+        {
+            convertor.toJSON(obj, out);
+        }
+    }
+
+    public Object fromJSON(Map object)
+    {
+        Map map=object;
+        String clsName=(String)map.get("class");
+        if (clsName!=null)
+        {
+            Convertor convertor=_json.getConvertorFor(clsName);
+            if (convertor==null)
+            {
+                try
+                {
+                    Class cls=Loader.loadClass(JSON.class,clsName);
+                    convertor=new JSONPojoConvertor(cls,_fromJson);
+                    _json.addConvertorFor(clsName, convertor);
+                }
+                catch (ClassNotFoundException e)
+                {
+                    JSON.LOG.warn(e);
+                }
+            }
+            if (convertor!=null)
+            {
+                return convertor.fromJSON(object);
+            }
+        }
+        return map;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/component/AbstractLifeCycle.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,217 @@
+//
+//  ========================================================================
+//  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.util.component;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * Basic implementation of the life cycle interface for components.
+ * 
+ * 
+ */
+public abstract class AbstractLifeCycle implements LifeCycle
+{
+    private static final Logger LOG = Log.getLogger(AbstractLifeCycle.class);
+    public static final String STOPPED="STOPPED";
+    public static final String FAILED="FAILED";
+    public static final String STARTING="STARTING";
+    public static final String STARTED="STARTED";
+    public static final String STOPPING="STOPPING";
+    public static final String RUNNING="RUNNING";
+    
+    private final Object _lock = new Object();
+    private final int __FAILED = -1, __STOPPED = 0, __STARTING = 1, __STARTED = 2, __STOPPING = 3;
+    private volatile int _state = __STOPPED;
+    
+    protected final CopyOnWriteArrayList<LifeCycle.Listener> _listeners=new CopyOnWriteArrayList<LifeCycle.Listener>();
+
+    protected void doStart() throws Exception
+    {
+    }
+
+    protected void doStop() throws Exception
+    {
+    }
+
+    public final void start() throws Exception
+    {
+        synchronized (_lock)
+        {
+            try
+            {
+                if (_state == __STARTED || _state == __STARTING)
+                    return;
+                setStarting();
+                doStart();
+                setStarted();
+            }
+            catch (Exception e)
+            {
+                setFailed(e);
+                throw e;
+            }
+            catch (Error e)
+            {
+                setFailed(e);
+                throw e;
+            }
+        }
+    }
+
+    public final void stop() throws Exception
+    {
+        synchronized (_lock)
+        {
+            try
+            {
+                if (_state == __STOPPING || _state == __STOPPED)
+                    return;
+                setStopping();
+                doStop();
+                setStopped();
+            }
+            catch (Exception e)
+            {
+                setFailed(e);
+                throw e;
+            }
+            catch (Error e)
+            {
+                setFailed(e);
+                throw e;
+            }
+        }
+    }
+
+    public boolean isRunning()
+    {
+        final int state = _state;
+        
+        return state == __STARTED || state == __STARTING;
+    }
+
+    public boolean isStarted()
+    {
+        return _state == __STARTED;
+    }
+
+    public boolean isStarting()
+    {
+        return _state == __STARTING;
+    }
+
+    public boolean isStopping()
+    {
+        return _state == __STOPPING;
+    }
+
+    public boolean isStopped()
+    {
+        return _state == __STOPPED;
+    }
+
+    public boolean isFailed()
+    {
+        return _state == __FAILED;
+    }
+
+    public void addLifeCycleListener(LifeCycle.Listener listener)
+    {
+        _listeners.add(listener);
+    }
+
+    public void removeLifeCycleListener(LifeCycle.Listener listener)
+    {
+        _listeners.remove(listener);
+    }
+    
+    public String getState()
+    {
+        switch(_state)
+        {
+            case __FAILED: return FAILED;
+            case __STARTING: return STARTING;
+            case __STARTED: return STARTED;
+            case __STOPPING: return STOPPING;
+            case __STOPPED: return STOPPED;
+        }
+        return null;
+    }
+    
+    public static String getState(LifeCycle lc)
+    {
+        if (lc.isStarting()) return STARTING;
+        if (lc.isStarted()) return STARTED;
+        if (lc.isStopping()) return STOPPING;
+        if (lc.isStopped()) return STOPPED;
+        return FAILED;
+    }
+
+    private void setStarted()
+    {
+        _state = __STARTED;
+        LOG.debug(STARTED+" {}",this);
+        for (Listener listener : _listeners)
+            listener.lifeCycleStarted(this);
+    }
+
+    private void setStarting()
+    {
+        LOG.debug("starting {}",this);
+        _state = __STARTING;
+        for (Listener listener : _listeners)
+            listener.lifeCycleStarting(this);
+    }
+
+    private void setStopping()
+    {
+        LOG.debug("stopping {}",this);
+        _state = __STOPPING;
+        for (Listener listener : _listeners)
+            listener.lifeCycleStopping(this);
+    }
+
+    private void setStopped()
+    {
+        _state = __STOPPED;
+        LOG.debug("{} {}",STOPPED,this);
+        for (Listener listener : _listeners)
+            listener.lifeCycleStopped(this);
+    }
+
+    private void setFailed(Throwable th)
+    {
+        _state = __FAILED;
+        LOG.warn(FAILED+" " + this+": "+th,th);
+        for (Listener listener : _listeners)
+            listener.lifeCycleFailure(this,th);
+    }
+
+    public static abstract class AbstractLifeCycleListener implements LifeCycle.Listener
+    {
+        public void lifeCycleFailure(LifeCycle event, Throwable cause) {}
+        public void lifeCycleStarted(LifeCycle event) {}
+        public void lifeCycleStarting(LifeCycle event) {}
+        public void lifeCycleStopped(LifeCycle event) {}
+        public void lifeCycleStopping(LifeCycle event) {}
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/component/AggregateLifeCycle.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,441 @@
+//
+//  ========================================================================
+//  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.util.component;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * An AggregateLifeCycle is an {@link LifeCycle} implementation for a collection of contained beans.
+ * <p>
+ * Beans can be added the AggregateLifeCycle either as managed beans or as unmanaged beans.  A managed bean is started, stopped and destroyed with the aggregate.  
+ * An unmanaged bean is associated with the aggregate for the purposes of {@link #dump()}, but it's lifecycle must be managed externally.
+ * <p>
+ * When a bean is added, if it is a {@link LifeCycle} and it is already started, then it is assumed to be an unmanaged bean.  
+ * Otherwise the methods {@link #addBean(Object, boolean)}, {@link #manage(Object)} and {@link #unmanage(Object)} can be used to 
+ * explicitly control the life cycle relationship.
+ * <p>
+ * If adding a bean that is shared between multiple {@link AggregateLifeCycle} instances, then it should be started before being added, so it is unmanaged, or 
+ * the API must be used to explicitly set it as unmanaged.
+ * <p>
+ */
+public class AggregateLifeCycle extends AbstractLifeCycle implements Destroyable, Dumpable
+{
+    private static final Logger LOG = Log.getLogger(AggregateLifeCycle.class);
+    private final List<Bean> _beans=new CopyOnWriteArrayList<Bean>();
+    private boolean _started=false;
+
+    private class Bean
+    {
+        Bean(Object b) 
+        {
+            _bean=b;
+        }
+        final Object _bean;
+        volatile boolean _managed=true;
+        
+        public String toString()
+        {
+            return "{"+_bean+","+_managed+"}";
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Start the managed lifecycle beans in the order they were added.
+     * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
+     */
+    @Override
+    protected void doStart() throws Exception
+    {
+        for (Bean b:_beans)
+        {
+            if (b._managed && b._bean instanceof LifeCycle)
+            {
+                LifeCycle l=(LifeCycle)b._bean;
+                if (!l.isRunning())
+                    l.start();
+            }
+        }
+        // indicate that we are started, so that addBean will start other beans added.
+        _started=true;
+        super.doStart();
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Stop the joined lifecycle beans in the reverse order they were added.
+     * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
+     */
+    @Override
+    protected void doStop() throws Exception
+    {
+        _started=false;
+        super.doStop();
+        List<Bean> reverse = new ArrayList<Bean>(_beans);
+        Collections.reverse(reverse);
+        for (Bean b:reverse)
+        {
+            if (b._managed && b._bean instanceof LifeCycle)
+            {
+                LifeCycle l=(LifeCycle)b._bean;
+                if (l.isRunning())
+                    l.stop();
+            }
+        }
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Destroy the joined Destroyable beans in the reverse order they were added.
+     * @see org.eclipse.jetty.util.component.Destroyable#destroy()
+     */
+    public void destroy()
+    {
+        List<Bean> reverse = new ArrayList<Bean>(_beans);
+        Collections.reverse(reverse);
+        for (Bean b:reverse)
+        {
+            if (b._bean instanceof Destroyable && b._managed)
+            {
+                Destroyable d=(Destroyable)b._bean;
+                d.destroy();
+            }
+        }
+        _beans.clear();
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /** Is the bean contained in the aggregate.
+     * @param bean
+     * @return True if the aggregate contains the bean
+     */
+    public boolean contains(Object bean)
+    {
+        for (Bean b:_beans)
+            if (b._bean==bean)
+                return true;
+        return false;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Is the bean joined to the aggregate.
+     * @param bean
+     * @return True if the aggregate contains the bean and it is joined
+     */
+    public boolean isManaged(Object bean)
+    {
+        for (Bean b:_beans)
+            if (b._bean==bean)
+                return b._managed;
+        return false;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Add an associated bean.
+     * If the bean is a {@link LifeCycle}, then it will be managed if it is not 
+     * already started and umanaged if it is already started. The {@link #addBean(Object, boolean)}
+     * method should be used if this is not correct, or the {@link #manage(Object)} and {@link #unmanage(Object)}
+     * methods may be used after an add to change the status.
+     * @param o the bean object to add
+     * @return true if the bean was added or false if it has already been added.
+     */
+    public boolean addBean(Object o)
+    {
+        // beans are joined unless they are started lifecycles
+        return addBean(o,!((o instanceof LifeCycle)&&((LifeCycle)o).isStarted()));
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Add an associated lifecycle.
+     * @param o The lifecycle to add
+     * @param managed True if the LifeCycle is to be joined, otherwise it will be disjoint.
+     * @return true if bean was added, false if already present.
+     */
+    public boolean addBean(Object o, boolean managed)
+    {
+        if (contains(o))
+            return false;
+        
+        Bean b = new Bean(o);
+        b._managed=managed;
+        _beans.add(b);
+        
+        if (o instanceof LifeCycle)
+        {
+            LifeCycle l=(LifeCycle)o;
+
+            // Start the bean if we are started
+            if (managed && _started)
+            {
+                try
+                {
+                    l.start();
+                }
+                catch(Exception e)
+                {
+                    throw new RuntimeException (e);
+                }
+            }
+        }
+        return true;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Manage a bean by this aggregate, so that it is started/stopped/destroyed with the 
+     * aggregate lifecycle.  
+     * @param bean The bean to manage (must already have been added).
+     */
+    public void manage(Object bean)
+    {    
+        for (Bean b :_beans)
+        {
+            if (b._bean==bean)
+            {
+                b._managed=true;
+                return;
+            }
+        }
+        throw new IllegalArgumentException();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Unmanage a bean by this aggregate, so that it is not started/stopped/destroyed with the 
+     * aggregate lifecycle.  
+     * @param bean The bean to manage (must already have been added).
+     */
+    public void unmanage(Object bean)
+    {
+        for (Bean b :_beans)
+        {
+            if (b._bean==bean)
+            {
+                b._managed=false;
+                return;
+            }
+        }
+        throw new IllegalArgumentException();
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Get dependent beans 
+     * @return List of beans.
+     */
+    public Collection<Object> getBeans()
+    {
+        return getBeans(Object.class);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Get dependent beans of a specific class
+     * @see #addBean(Object)
+     * @param clazz
+     * @return List of beans.
+     */
+    public <T> List<T> getBeans(Class<T> clazz)
+    {
+        ArrayList<T> beans = new ArrayList<T>();
+        for (Bean b:_beans)
+        {
+            if (clazz.isInstance(b._bean))
+                beans.add((T)(b._bean));
+        }
+        return beans;
+    }
+
+    
+    /* ------------------------------------------------------------ */
+    /** Get dependent beans of a specific class.
+     * If more than one bean of the type exist, the first is returned.
+     * @see #addBean(Object)
+     * @param clazz
+     * @return bean or null
+     */
+    public <T> T getBean(Class<T> clazz)
+    {
+        for (Bean b:_beans)
+        {
+            if (clazz.isInstance(b._bean))
+                return (T)b._bean;
+        }
+        
+        return null;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Remove all associated bean.
+     */
+    public void removeBeans ()
+    {
+        _beans.clear();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Remove an associated bean.
+     */
+    public boolean removeBean (Object o)
+    {
+        Iterator<Bean> i = _beans.iterator();
+        while(i.hasNext())
+        {
+            Bean b=i.next();
+            if (b._bean==o)
+            {
+                _beans.remove(b);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void dumpStdErr()
+    {
+        try
+        {
+            dump(System.err,"");
+        }
+        catch (IOException e)
+        {
+            LOG.warn(e);
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    public String dump()
+    {
+        return dump(this);
+    }    
+    
+    /* ------------------------------------------------------------ */
+    public static String dump(Dumpable dumpable)
+    {
+        StringBuilder b = new StringBuilder();
+        try
+        {
+            dumpable.dump(b,"");
+        }
+        catch (IOException e)
+        {
+            LOG.warn(e);
+        }
+        return b.toString();
+    }    
+
+    /* ------------------------------------------------------------ */
+    public void dump(Appendable out) throws IOException
+    {
+        dump(out,"");
+    }
+
+    /* ------------------------------------------------------------ */
+    protected void dumpThis(Appendable out) throws IOException
+    {
+        out.append(String.valueOf(this)).append(" - ").append(getState()).append("\n");
+    }
+
+    /* ------------------------------------------------------------ */
+    public static void dumpObject(Appendable out,Object o) throws IOException
+    {
+        try
+        {
+            if (o instanceof LifeCycle)
+                out.append(String.valueOf(o)).append(" - ").append((AbstractLifeCycle.getState((LifeCycle)o))).append("\n");
+            else
+                out.append(String.valueOf(o)).append("\n");
+        }
+        catch(Throwable th)
+        {
+            out.append(" => ").append(th.toString()).append('\n');
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    public void dump(Appendable out,String indent) throws IOException
+    {
+        dumpThis(out);
+        int size=_beans.size();
+        if (size==0)
+            return;
+        int i=0;
+        for (Bean b : _beans)
+        {
+            i++;
+
+            out.append(indent).append(" +- ");
+            if (b._managed)
+            {
+                if (b._bean instanceof Dumpable)
+                    ((Dumpable)b._bean).dump(out,indent+(i==size?"    ":" |  "));
+                else 
+                    dumpObject(out,b._bean);
+            }
+            else 
+                dumpObject(out,b._bean);
+        }
+
+        if (i!=size)
+            out.append(indent).append(" |\n");
+    }
+    
+    /* ------------------------------------------------------------ */
+    public static void dump(Appendable out,String indent,Collection<?>... collections) throws IOException
+    {
+        if (collections.length==0)
+            return;
+        int size=0;
+        for (Collection<?> c : collections)
+            size+=c.size();    
+        if (size==0)
+            return;
+
+        int i=0;
+        for (Collection<?> c : collections)
+        {
+            for (Object o : c)
+            {
+                i++;
+                out.append(indent).append(" +- ");
+
+                if (o instanceof Dumpable)
+                    ((Dumpable)o).dump(out,indent+(i==size?"    ":" |  "));
+                else 
+                    dumpObject(out,o);
+            }
+            
+            if (i!=size)
+                out.append(indent).append(" |\n");          
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/component/Container.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,305 @@
+//
+//  ========================================================================
+//  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.util.component;
+import java.lang.ref.WeakReference;
+import java.util.EventListener;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.eclipse.jetty.util.LazyList;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/* ------------------------------------------------------------ */
+/** Container.
+ * This class allows a containment events to be generated from update methods.
+ * 
+ * The style of usage is: <pre>
+ *   public void setFoo(Foo foo)
+ *   {
+ *       getContainer().update(this,this.foo,foo,"foo");
+ *       this.foo=foo;
+ *   }
+ *   
+ *   public void setBars(Bar[] bars)
+ *   {
+ *       getContainer().update(this,this.bars,bars,"bar");
+ *       this.bars=bars;
+ *   }
+ * </pre>
+ */
+public class Container
+{
+    private static final Logger LOG = Log.getLogger(Container.class);
+    private final CopyOnWriteArrayList<Container.Listener> _listeners=new CopyOnWriteArrayList<Container.Listener>();
+    
+    public void addEventListener(Container.Listener listener)
+    {
+        _listeners.add(listener);
+    }
+    
+    public void removeEventListener(Container.Listener listener)
+    {
+        _listeners.remove(listener);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Update single parent to child relationship.
+     * @param parent The parent of the child.
+     * @param oldChild The previous value of the child.  If this is non null and differs from <code>child</code>, then a remove event is generated.
+     * @param child The current child. If this is non null and differs from <code>oldChild</code>, then an add event is generated.
+     * @param relationship The name of the relationship
+     */
+    public void update(Object parent, Object oldChild, final Object child, String relationship)
+    {
+        if (oldChild!=null && !oldChild.equals(child))
+            remove(parent,oldChild,relationship);
+        if (child!=null && !child.equals(oldChild))
+            add(parent,child,relationship);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Update single parent to child relationship.
+     * @param parent The parent of the child.
+     * @param oldChild The previous value of the child.  If this is non null and differs from <code>child</code>, then a remove event is generated.
+     * @param child The current child. If this is non null and differs from <code>oldChild</code>, then an add event is generated.
+     * @param relationship The name of the relationship
+     * @param addRemove If true add/remove is called for the new/old children as well as the relationships
+     */
+    public void update(Object parent, Object oldChild, final Object child, String relationship,boolean addRemove)
+    {
+        if (oldChild!=null && !oldChild.equals(child))
+        {
+            remove(parent,oldChild,relationship);
+            if (addRemove)
+                removeBean(oldChild);
+        }
+        
+        if (child!=null && !child.equals(oldChild))
+        {
+            if (addRemove)
+                addBean(child);
+            add(parent,child,relationship);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Update multiple parent to child relationship.
+     * @param parent The parent of the child.
+     * @param oldChildren The previous array of children.  A remove event is generated for any child in this array but not in the  <code>children</code> array.
+     * This array is modified and children that remain in the new children array are nulled out of the old children array.
+     * @param children The current array of children. An add event is generated for any child in this array but not in the <code>oldChildren</code> array.
+     * @param relationship The name of the relationship
+     */
+    public void update(Object parent, Object[] oldChildren, final Object[] children, String relationship)
+    {
+        update(parent,oldChildren,children,relationship,false);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Update multiple parent to child relationship.
+     * @param parent The parent of the child.
+     * @param oldChildren The previous array of children.  A remove event is generated for any child in this array but not in the  <code>children</code> array.
+     * This array is modified and children that remain in the new children array are nulled out of the old children array.
+     * @param children The current array of children. An add event is generated for any child in this array but not in the <code>oldChildren</code> array.
+     * @param relationship The name of the relationship
+     * @param addRemove If true add/remove is called for the new/old children as well as the relationships
+     */
+    public void update(Object parent, Object[] oldChildren, final Object[] children, String relationship, boolean addRemove)
+    {
+        Object[] newChildren = null;
+        if (children!=null)
+        {
+            newChildren = new Object[children.length];
+        
+            for (int i=children.length;i-->0;)
+            {
+                boolean new_child=true;
+                if (oldChildren!=null)
+                {
+                    for (int j=oldChildren.length;j-->0;)
+                    {
+                        if (children[i]!=null && children[i].equals(oldChildren[j]))
+                        {
+                            oldChildren[j]=null;
+                            new_child=false;
+                        }
+                    }
+                }
+                if (new_child)
+                    newChildren[i]=children[i];
+            }
+        }
+        
+        if (oldChildren!=null)
+        {
+            for (int i=oldChildren.length;i-->0;)
+            {
+                if (oldChildren[i]!=null)
+                {
+                    remove(parent,oldChildren[i],relationship);
+                    if (addRemove)
+                        removeBean(oldChildren[i]);
+                }
+            }
+        }
+        
+        if (newChildren!=null)
+        {
+            for (int i=0;i<newChildren.length;i++)
+                if (newChildren[i]!=null)
+                {
+                    if (addRemove)
+                        addBean(newChildren[i]);
+                    add(parent,newChildren[i],relationship);
+                }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public void addBean(Object obj)
+    {
+        if (_listeners!=null)
+        {
+            for (int i=0; i<LazyList.size(_listeners); i++)
+            {
+                Listener listener=(Listener)LazyList.get(_listeners, i);
+                listener.addBean(obj);
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public void removeBean(Object obj)
+    {
+        if (_listeners!=null)
+        {
+            for (int i=0; i<LazyList.size(_listeners); i++)
+                ((Listener)LazyList.get(_listeners, i)).removeBean(obj);
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Add a parent child relationship
+     * @param parent
+     * @param child
+     * @param relationship
+     */
+    private void add(Object parent, Object child, String relationship)
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("Container "+parent+" + "+child+" as "+relationship);
+        if (_listeners!=null)
+        {
+            Relationship event=new Relationship(this,parent,child,relationship);
+            for (int i=0; i<LazyList.size(_listeners); i++)
+                ((Listener)LazyList.get(_listeners, i)).add(event);
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** remove a parent child relationship
+     * @param parent
+     * @param child
+     * @param relationship
+     */
+    private void remove(Object parent, Object child, String relationship)
+    {
+        if (LOG.isDebugEnabled())
+            LOG.debug("Container "+parent+" - "+child+" as "+relationship);
+        if (_listeners!=null)
+        {
+            Relationship event=new Relationship(this,parent,child,relationship);
+            for (int i=0; i<LazyList.size(_listeners); i++)
+                ((Listener)LazyList.get(_listeners, i)).remove(event);
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** A Container event.
+     * @see Listener
+     */
+    public static class Relationship
+    {
+        private final WeakReference<Object> _parent;
+        private final WeakReference<Object> _child;
+        private String _relationship;
+        private Container _container;
+        
+        private Relationship(Container container, Object parent,Object child, String relationship)
+        {
+            _container=container;
+            _parent=new WeakReference<Object>(parent);
+            _child=new WeakReference<Object>(child);
+            _relationship=relationship;
+        }
+        
+        public Container getContainer()
+        {
+            return _container;
+        }
+        
+        public Object getChild()
+        {
+            return _child.get();
+        }
+        
+        public Object getParent()
+        {
+            return _parent.get();
+        }
+        
+        public String getRelationship()
+        {
+            return _relationship;
+        }
+        
+        @Override
+        public String toString()
+        {
+            return _parent+"---"+_relationship+"-->"+_child;
+        }
+        
+        @Override
+        public int hashCode()
+        {
+            return _parent.hashCode()+_child.hashCode()+_relationship.hashCode();
+        }
+        
+        @Override
+        public boolean equals(Object o)
+        {
+            if (o==null || !(o instanceof Relationship))
+                return false;
+            Relationship r = (Relationship)o;
+            return r._parent.get()==_parent.get() && r._child.get()==_child.get() && r._relationship.equals(_relationship);
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Listener.
+     * A listener for Container events.
+     */
+    public interface Listener extends EventListener
+    {
+        public void addBean(Object bean);
+        public void removeBean(Object bean);
+        public void add(Container.Relationship relationship);
+        public void remove(Container.Relationship relationship);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/component/Destroyable.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,31 @@
+//
+//  ========================================================================
+//  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.util.component;
+
+
+/**
+ * <p>A Destroyable is an object which can be destroyed.</p>
+ * <p>Typically a Destroyable is a {@link LifeCycle} component that can hold onto
+ * resources over multiple start/stop cycles.   A call to destroy will release all
+ * resources and will prevent any further start/stop cycles from being successful.</p>
+ */
+public interface Destroyable
+{
+    void destroy();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/component/Dumpable.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,27 @@
+//
+//  ========================================================================
+//  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.util.component;
+
+import java.io.IOException;
+
+public interface Dumpable
+{
+    String dump();
+    void dump(Appendable out,String indent) throws IOException;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/component/FileDestroyable.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,88 @@
+//
+//  ========================================================================
+//  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.util.component;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.resource.Resource;
+
+public class FileDestroyable implements Destroyable
+{
+    private static final Logger LOG = Log.getLogger(FileDestroyable.class);
+    final List<File> _files = new ArrayList<File>();
+
+    public FileDestroyable()
+    {
+    }
+    
+    public FileDestroyable(String file) throws IOException
+    {
+        _files.add(Resource.newResource(file).getFile());
+    }
+    
+    public FileDestroyable(File file)
+    {
+        _files.add(file);
+    }
+    
+    public void addFile(String file) throws IOException
+    {
+        _files.add(Resource.newResource(file).getFile());
+    }
+    
+    public void addFile(File file)
+    {
+        _files.add(file);
+    }
+    
+    public void addFiles(Collection<File> files)
+    {
+        _files.addAll(files);
+    }
+    
+    public void removeFile(String file) throws IOException
+    {
+        _files.remove(Resource.newResource(file).getFile());
+    }
+    
+    public void removeFile(File file)
+    {
+        _files.remove(file);
+    }
+    
+    public void destroy()
+    {
+        for (File file : _files)
+        {
+            if (file.exists())
+            {
+                LOG.debug("Destroy {}",file);
+                IO.delete(file);
+            }
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/component/FileNoticeLifeCycleListener.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,80 @@
+//
+//  ========================================================================
+//  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.util.component;
+
+import java.io.FileWriter;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+
+/* ------------------------------------------------------------ */
+/** A LifeCycle Listener that writes state changes to a file.
+ * <p>This can be used with the jetty.sh script to wait for successful startup.
+ */
+public class FileNoticeLifeCycleListener implements LifeCycle.Listener
+{
+    Logger LOG = Log.getLogger(FileNoticeLifeCycleListener.class);
+    
+    private final String _filename;
+    
+    public FileNoticeLifeCycleListener(String filename)
+    {
+        _filename=filename;
+    }
+
+    private void writeState(String action, LifeCycle lifecycle)
+    {
+        try
+        {
+            FileWriter out = new FileWriter(_filename,true);
+            out.append(action).append(" ").append(lifecycle.toString()).append("\n");
+            out.close();
+        }
+        catch(Exception e)
+        {
+            LOG.warn(e);
+        }
+    }
+    
+    public void lifeCycleStarting(LifeCycle event)
+    {  
+        writeState("STARTING",event);      
+    }
+
+    public void lifeCycleStarted(LifeCycle event)
+    {        
+        writeState("STARTED",event); 
+    }
+
+    public void lifeCycleFailure(LifeCycle event, Throwable cause)
+    {        
+        writeState("FAILED",event);
+    }
+
+    public void lifeCycleStopping(LifeCycle event)
+    {        
+        writeState("STOPPING",event);
+    }
+
+    public void lifeCycleStopped(LifeCycle event)
+    {        
+        writeState("STOPPED",event);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/component/LifeCycle.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,119 @@
+//
+//  ========================================================================
+//  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.util.component;
+
+import java.util.EventListener;
+
+/* ------------------------------------------------------------ */
+/**
+ * The lifecycle interface for generic components.
+ * <br />
+ * Classes implementing this interface have a defined life cycle
+ * defined by the methods of this interface.
+ *
+ * 
+ */
+public interface LifeCycle
+{
+    /* ------------------------------------------------------------ */
+    /**
+     * Starts the component.
+     * @throws Exception If the component fails to start
+     * @see #isStarted()
+     * @see #stop()
+     * @see #isFailed()
+     */
+    public void start()
+        throws Exception;
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Stops the component.
+     * The component may wait for current activities to complete
+     * normally, but it can be interrupted.
+     * @exception Exception If the component fails to stop
+     * @see #isStopped()
+     * @see #start()
+     * @see #isFailed()
+     */
+    public void stop()
+        throws Exception;
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return true if the component is starting or has been started.
+     */
+    public boolean isRunning();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return true if the component has been started.
+     * @see #start()
+     * @see #isStarting()
+     */
+    public boolean isStarted();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return true if the component is starting.
+     * @see #isStarted()
+     */
+    public boolean isStarting();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return true if the component is stopping.
+     * @see #isStopped()
+     */
+    public boolean isStopping();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return true if the component has been stopped.
+     * @see #stop()
+     * @see #isStopping()
+     */
+    public boolean isStopped();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return true if the component has failed to start or has failed to stop.
+     */
+    public boolean isFailed();
+    
+    /* ------------------------------------------------------------ */
+    public void addLifeCycleListener(LifeCycle.Listener listener);
+
+    /* ------------------------------------------------------------ */
+    public void removeLifeCycleListener(LifeCycle.Listener listener);
+    
+
+    /* ------------------------------------------------------------ */
+    /** Listener.
+     * A listener for Lifecycle events.
+     */
+    public interface Listener extends EventListener
+    {
+        public void lifeCycleStarting(LifeCycle event);
+        public void lifeCycleStarted(LifeCycle event);
+        public void lifeCycleFailure(LifeCycle event,Throwable cause);
+        public void lifeCycleStopping(LifeCycle event);
+        public void lifeCycleStopped(LifeCycle event);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/component/jmx/AggregateLifeCycle-mbean.properties	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,2 @@
+AggregateLifeCycle: A LifeCycle holding other LifeCycles
+dumpStdErr():Object:INFO:Dump the nested Object state to StdErr
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/component/jmx/Dumpable-mbean.properties	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,2 @@
+Dumpable: Dumpable Object
+dump():Object:INFO:Dump the nested Object state as a String
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/component/jmx/LifeCycle-mbean.properties	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,9 @@
+LifeCycle: Startable object
+start(): Starts the instance
+stop(): Stops the instance
+running: Instance is started or starting
+started: Instance is started
+starting: Instance is starting
+stopping: Instance is stopping
+stopped: Instance is stopped
+failed: Instance is failed
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/log/AbstractLogger.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,77 @@
+//
+//  ========================================================================
+//  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.util.log;
+
+
+/* ------------------------------------------------------------ */
+/** Abstract Logger.
+ * Manages the atomic registration of the logger by name.
+ */
+public abstract class AbstractLogger implements Logger
+{
+    public final Logger getLogger(String name)
+    {
+        if (isBlank(name))
+            return this;
+
+        final String basename = getName();
+        final String fullname = (isBlank(basename) || Log.getRootLogger()==this)?name:(basename + "." + name);
+        
+        Logger logger = Log.getLoggers().get(fullname);
+        if (logger == null)
+        {
+            Logger newlog = newLogger(fullname);
+            
+            logger = Log.getMutableLoggers().putIfAbsent(fullname,newlog);
+            if (logger == null)
+                logger=newlog;
+        }
+
+        return logger;
+    }
+    
+
+    protected abstract Logger newLogger(String fullname);
+
+    /**
+     * A more robust form of name blank test. Will return true for null names, and names that have only whitespace
+     *
+     * @param name
+     *            the name to test
+     * @return true for null or blank name, false if any non-whitespace character is found.
+     */
+    private static boolean isBlank(String name)
+    {
+        if (name == null)
+        {
+            return true;
+        }
+        int size = name.length();
+        char c;
+        for (int i = 0; i < size; i++)
+        {
+            c = name.charAt(i);
+            if (!Character.isWhitespace(c))
+            {
+                return false;
+            }
+        }
+        return true;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/log/JavaUtilLog.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,163 @@
+//
+//  ========================================================================
+//  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.util.log;
+
+import java.util.logging.Level;
+
+/**
+ * <p>
+ * Implementation of Jetty {@link Logger} based on {@link java.util.logging.Logger}.
+ * </p>
+ *
+ * <p>
+ * You can also set the logger level using <a href="http://java.sun.com/j2se/1.5.0/docs/guide/logging/overview.html">
+ * standard java.util.logging configuration</a>.
+ * </p>
+ */
+public class JavaUtilLog extends AbstractLogger
+{
+    private Level configuredLevel;
+    private java.util.logging.Logger _logger;
+
+    public JavaUtilLog()
+    {
+        this("org.eclipse.jetty.util.log");
+    }
+
+    public JavaUtilLog(String name)
+    {
+        _logger = java.util.logging.Logger.getLogger(name);
+        if (Boolean.parseBoolean(Log.__props.getProperty("org.eclipse.jetty.util.log.DEBUG", "false")))
+        {
+            _logger.setLevel(Level.FINE);
+        }
+        configuredLevel = _logger.getLevel();
+    }
+
+    public String getName()
+    {
+        return _logger.getName();
+    }
+
+    public void warn(String msg, Object... args)
+    {
+        _logger.log(Level.WARNING, format(msg, args));
+    }
+
+    public void warn(Throwable thrown)
+    {
+        warn("", thrown);
+    }
+
+    public void warn(String msg, Throwable thrown)
+    {
+        _logger.log(Level.WARNING, msg, thrown);
+    }
+
+    public void info(String msg, Object... args)
+    {
+        _logger.log(Level.INFO, format(msg, args));
+    }
+
+    public void info(Throwable thrown)
+    {
+        info("", thrown);
+    }
+
+    public void info(String msg, Throwable thrown)
+    {
+        _logger.log(Level.INFO, msg, thrown);
+    }
+
+    public boolean isDebugEnabled()
+    {
+        return _logger.isLoggable(Level.FINE);
+    }
+
+    public void setDebugEnabled(boolean enabled)
+    {
+        if (enabled)
+        {
+            configuredLevel = _logger.getLevel();
+            _logger.setLevel(Level.FINE);
+        }
+        else
+        {
+            _logger.setLevel(configuredLevel);
+        }
+    }
+
+    public void debug(String msg, Object... args)
+    {
+        _logger.log(Level.FINE, format(msg, args));
+    }
+
+    public void debug(Throwable thrown)
+    {
+        debug("", thrown);
+    }
+
+    public void debug(String msg, Throwable thrown)
+    {
+        _logger.log(Level.FINE, msg, thrown);
+    }
+
+    /**
+     * Create a Child Logger of this Logger.
+     */
+    protected Logger newLogger(String fullname)
+    {
+        return new JavaUtilLog(fullname);
+    }
+
+    public void ignore(Throwable ignored)
+    {
+        if (Log.isIgnored())
+        {
+            warn(Log.IGNORED, ignored);
+        }
+    }
+
+    private String format(String msg, Object... args)
+    {
+        msg = String.valueOf(msg); // Avoids NPE
+        String braces = "{}";
+        StringBuilder builder = new StringBuilder();
+        int start = 0;
+        for (Object arg : args)
+        {
+            int bracesIndex = msg.indexOf(braces, start);
+            if (bracesIndex < 0)
+            {
+                builder.append(msg.substring(start));
+                builder.append(" ");
+                builder.append(arg);
+                start = msg.length();
+            }
+            else
+            {
+                builder.append(msg.substring(start, bracesIndex));
+                builder.append(String.valueOf(arg));
+                start = bracesIndex + braces.length();
+            }
+        }
+        builder.append(msg.substring(start));
+        return builder.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/log/JettyAwareLogger.java	Wed Sep 07 21:15:48 2016 -0600
@@ -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.util.log;
+
+import org.slf4j.Marker;
+import org.slf4j.helpers.FormattingTuple;
+import org.slf4j.helpers.MessageFormatter;
+
+/**
+ * JettyAwareLogger is used to fix a FQCN bug that arises from how Jetty
+ * Log uses an indirect slf4j implementation.
+ *
+ * https://bugs.eclipse.org/bugs/show_bug.cgi?id=276670
+ */
+class JettyAwareLogger implements org.slf4j.Logger
+{
+    private static final int DEBUG = org.slf4j.spi.LocationAwareLogger.DEBUG_INT;
+    private static final int ERROR = org.slf4j.spi.LocationAwareLogger.ERROR_INT;
+    private static final int INFO  = org.slf4j.spi.LocationAwareLogger.INFO_INT;
+    private static final int TRACE = org.slf4j.spi.LocationAwareLogger.TRACE_INT;
+    private static final int WARN  = org.slf4j.spi.LocationAwareLogger.WARN_INT;
+
+    private static final String FQCN = Slf4jLog.class.getName();
+    private final org.slf4j.spi.LocationAwareLogger _logger;
+
+    public JettyAwareLogger(org.slf4j.spi.LocationAwareLogger logger)
+    {
+        _logger = logger;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#getName()
+     */
+    public String getName()
+    {
+        return _logger.getName();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#isTraceEnabled()
+     */
+    public boolean isTraceEnabled()
+    {
+        return _logger.isTraceEnabled();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#trace(java.lang.String)
+     */
+    public void trace(String msg)
+    {
+        log(null, TRACE, msg, null, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#trace(java.lang.String, java.lang.Object)
+     */
+    public void trace(String format, Object arg)
+    {
+        log(null, TRACE, format, new Object[]{arg}, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#trace(java.lang.String, java.lang.Object, java.lang.Object)
+     */
+    public void trace(String format, Object arg1, Object arg2)
+    {
+        log(null, TRACE, format, new Object[]{arg1,arg2}, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#trace(java.lang.String, java.lang.Object[])
+     */
+    public void trace(String format, Object[] argArray)
+    {
+        log(null, TRACE, format, argArray, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#trace(java.lang.String, java.lang.Throwable)
+     */
+    public void trace(String msg, Throwable t)
+    {
+        log(null, TRACE, msg, null, t);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#isTraceEnabled(org.slf4j.Marker)
+     */
+    public boolean isTraceEnabled(Marker marker)
+    {
+        return _logger.isTraceEnabled(marker);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#trace(org.slf4j.Marker, java.lang.String)
+     */
+    public void trace(Marker marker, String msg)
+    {
+        log(marker, TRACE, msg, null, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#trace(org.slf4j.Marker, java.lang.String, java.lang.Object)
+     */
+    public void trace(Marker marker, String format, Object arg)
+    {
+        log(marker, TRACE, format, new Object[]{arg}, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#trace(org.slf4j.Marker, java.lang.String, java.lang.Object, java.lang.Object)
+     */
+    public void trace(Marker marker, String format, Object arg1, Object arg2)
+    {
+        log(marker, TRACE, format, new Object[]{arg1,arg2}, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#trace(org.slf4j.Marker, java.lang.String, java.lang.Object[])
+     */
+    public void trace(Marker marker, String format, Object[] argArray)
+    {
+        log(marker, TRACE, format, argArray, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#trace(org.slf4j.Marker, java.lang.String, java.lang.Throwable)
+     */
+    public void trace(Marker marker, String msg, Throwable t)
+    {
+        log(marker, TRACE, msg, null, t);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#isDebugEnabled()
+     */
+    public boolean isDebugEnabled()
+    {
+        return _logger.isDebugEnabled();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#debug(java.lang.String)
+     */
+    public void debug(String msg)
+    {
+        log(null, DEBUG, msg, null, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#debug(java.lang.String, java.lang.Object)
+     */
+    public void debug(String format, Object arg)
+    {
+        log(null, DEBUG, format, new Object[]{arg}, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#debug(java.lang.String, java.lang.Object, java.lang.Object)
+     */
+    public void debug(String format, Object arg1, Object arg2)
+    {
+        log(null, DEBUG, format, new Object[]{arg1,arg2}, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#debug(java.lang.String, java.lang.Object[])
+     */
+    public void debug(String format, Object[] argArray)
+    {
+        log(null, DEBUG, format, argArray, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#debug(java.lang.String, java.lang.Throwable)
+     */
+    public void debug(String msg, Throwable t)
+    {
+        log(null, DEBUG, msg, null, t);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#isDebugEnabled(org.slf4j.Marker)
+     */
+    public boolean isDebugEnabled(Marker marker)
+    {
+        return _logger.isDebugEnabled(marker);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#debug(org.slf4j.Marker, java.lang.String)
+     */
+    public void debug(Marker marker, String msg)
+    {
+        log(marker, DEBUG, msg, null, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#debug(org.slf4j.Marker, java.lang.String, java.lang.Object)
+     */
+    public void debug(Marker marker, String format, Object arg)
+    {
+        log(marker, DEBUG, format, new Object[]{arg}, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#debug(org.slf4j.Marker, java.lang.String, java.lang.Object, java.lang.Object)
+     */
+    public void debug(Marker marker, String format, Object arg1, Object arg2)
+    {
+        log(marker, DEBUG, format, new Object[]{arg1,arg2}, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#debug(org.slf4j.Marker, java.lang.String, java.lang.Object[])
+     */
+    public void debug(Marker marker, String format, Object[] argArray)
+    {
+        log(marker, DEBUG, format, argArray, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#debug(org.slf4j.Marker, java.lang.String, java.lang.Throwable)
+     */
+    public void debug(Marker marker, String msg, Throwable t)
+    {
+        log(marker, DEBUG, msg, null, t);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#isInfoEnabled()
+     */
+    public boolean isInfoEnabled()
+    {
+        return _logger.isInfoEnabled();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#info(java.lang.String)
+     */
+    public void info(String msg)
+    {
+        log(null, INFO, msg, null, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#info(java.lang.String, java.lang.Object)
+     */
+    public void info(String format, Object arg)
+    {
+        log(null, INFO, format, new Object[]{arg}, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#info(java.lang.String, java.lang.Object, java.lang.Object)
+     */
+    public void info(String format, Object arg1, Object arg2)
+    {
+        log(null, INFO, format, new Object[]{arg1,arg2}, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#info(java.lang.String, java.lang.Object[])
+     */
+    public void info(String format, Object[] argArray)
+    {
+        log(null, INFO, format, argArray, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#info(java.lang.String, java.lang.Throwable)
+     */
+    public void info(String msg, Throwable t)
+    {
+        log(null, INFO, msg, null, t);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#isInfoEnabled(org.slf4j.Marker)
+     */
+    public boolean isInfoEnabled(Marker marker)
+    {
+        return _logger.isInfoEnabled(marker);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#info(org.slf4j.Marker, java.lang.String)
+     */
+    public void info(Marker marker, String msg)
+    {
+        log(marker, INFO, msg, null, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#info(org.slf4j.Marker, java.lang.String, java.lang.Object)
+     */
+    public void info(Marker marker, String format, Object arg)
+    {
+        log(marker, INFO, format, new Object[]{arg}, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#info(org.slf4j.Marker, java.lang.String, java.lang.Object, java.lang.Object)
+     */
+    public void info(Marker marker, String format, Object arg1, Object arg2)
+    {
+        log(marker, INFO, format, new Object[]{arg1,arg2}, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#info(org.slf4j.Marker, java.lang.String, java.lang.Object[])
+     */
+    public void info(Marker marker, String format, Object[] argArray)
+    {
+        log(marker, INFO, format, argArray, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#info(org.slf4j.Marker, java.lang.String, java.lang.Throwable)
+     */
+    public void info(Marker marker, String msg, Throwable t)
+    {
+        log(marker, INFO, msg, null, t);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#isWarnEnabled()
+     */
+    public boolean isWarnEnabled()
+    {
+        return _logger.isWarnEnabled();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#warn(java.lang.String)
+     */
+    public void warn(String msg)
+    {
+        log(null, WARN, msg, null, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#warn(java.lang.String, java.lang.Object)
+     */
+    public void warn(String format, Object arg)
+    {
+        log(null, WARN, format, new Object[]{arg}, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#warn(java.lang.String, java.lang.Object[])
+     */
+    public void warn(String format, Object[] argArray)
+    {
+        log(null, WARN, format, argArray, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#warn(java.lang.String, java.lang.Object, java.lang.Object)
+     */
+    public void warn(String format, Object arg1, Object arg2)
+    {
+        log(null, WARN, format, new Object[]{arg1,arg2}, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#warn(java.lang.String, java.lang.Throwable)
+     */
+    public void warn(String msg, Throwable t)
+    {
+        log(null, WARN, msg, null, t);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#isWarnEnabled(org.slf4j.Marker)
+     */
+    public boolean isWarnEnabled(Marker marker)
+    {
+        return _logger.isWarnEnabled(marker);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#warn(org.slf4j.Marker, java.lang.String)
+     */
+    public void warn(Marker marker, String msg)
+    {
+        log(marker, WARN, msg, null, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#warn(org.slf4j.Marker, java.lang.String, java.lang.Object)
+     */
+    public void warn(Marker marker, String format, Object arg)
+    {
+        log(marker, WARN, format, new Object[]{arg}, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#warn(org.slf4j.Marker, java.lang.String, java.lang.Object, java.lang.Object)
+     */
+    public void warn(Marker marker, String format, Object arg1, Object arg2)
+    {
+        log(marker, WARN, format, new Object[]{arg1,arg2}, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#warn(org.slf4j.Marker, java.lang.String, java.lang.Object[])
+     */
+    public void warn(Marker marker, String format, Object[] argArray)
+    {
+        log(marker, WARN, format, argArray, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#warn(org.slf4j.Marker, java.lang.String, java.lang.Throwable)
+     */
+    public void warn(Marker marker, String msg, Throwable t)
+    {
+        log(marker, WARN, msg, null, t);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#isErrorEnabled()
+     */
+    public boolean isErrorEnabled()
+    {
+        return _logger.isErrorEnabled();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#error(java.lang.String)
+     */
+    public void error(String msg)
+    {
+        log(null, ERROR, msg, null, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#error(java.lang.String, java.lang.Object)
+     */
+    public void error(String format, Object arg)
+    {
+        log(null, ERROR, format, new Object[]{arg}, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#error(java.lang.String, java.lang.Object, java.lang.Object)
+     */
+    public void error(String format, Object arg1, Object arg2)
+    {
+        log(null, ERROR, format, new Object[]{arg1,arg2}, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#error(java.lang.String, java.lang.Object[])
+     */
+    public void error(String format, Object[] argArray)
+    {
+        log(null, ERROR, format, argArray, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#error(java.lang.String, java.lang.Throwable)
+     */
+    public void error(String msg, Throwable t)
+    {
+        log(null, ERROR, msg, null, t);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#isErrorEnabled(org.slf4j.Marker)
+     */
+    public boolean isErrorEnabled(Marker marker)
+    {
+        return _logger.isErrorEnabled(marker);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#error(org.slf4j.Marker, java.lang.String)
+     */
+    public void error(Marker marker, String msg)
+    {
+        log(marker, ERROR, msg, null, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#error(org.slf4j.Marker, java.lang.String, java.lang.Object)
+     */
+    public void error(Marker marker, String format, Object arg)
+    {
+        log(marker, ERROR, format, new Object[]{arg}, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#error(org.slf4j.Marker, java.lang.String, java.lang.Object, java.lang.Object)
+     */
+    public void error(Marker marker, String format, Object arg1, Object arg2)
+    {
+        log(marker, ERROR, format, new Object[]{arg1,arg2}, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#error(org.slf4j.Marker, java.lang.String, java.lang.Object[])
+     */
+    public void error(Marker marker, String format, Object[] argArray)
+    {
+        log(marker, ERROR, format, argArray, null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see org.slf4j.Logger#error(org.slf4j.Marker, java.lang.String, java.lang.Throwable)
+     */
+    public void error(Marker marker, String msg, Throwable t)
+    {
+        log(marker, ERROR, msg, null, t);
+    }
+
+    @Override
+    public String toString()
+    {
+        return _logger.toString();
+    }
+
+    private void log(Marker marker, int level, String msg, Object[] argArray, Throwable t)
+    {
+        if (argArray == null)
+        {
+            // Simple SLF4J Message (no args)
+            _logger.log(marker, FQCN, level, msg, null, t);
+        }
+        else
+        {
+            int loggerLevel = _logger.isTraceEnabled() ? TRACE :
+                    _logger.isDebugEnabled() ? DEBUG :
+                            _logger.isInfoEnabled() ? INFO :
+                                    _logger.isWarnEnabled() ? WARN : ERROR;
+            if (loggerLevel <= level)
+            {
+                // Don't assume downstream handles argArray properly.
+                // Do it the SLF4J way here to eliminate that as a bug.
+                FormattingTuple ft = MessageFormatter.arrayFormat(msg, argArray);
+                _logger.log(marker, FQCN, level, ft.getMessage(), null, t);
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/log/Log.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,462 @@
+//
+//  ========================================================================
+//  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.util.log;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.Loader;
+
+/**
+ * Logging.
+ * This class provides a static logging interface.  If an instance of the
+ * org.slf4j.Logger class is found on the classpath, the static log methods
+ * are directed to a slf4j logger for "org.eclipse.log".   Otherwise the logs
+ * are directed to stderr.
+ * <p>
+ * The "org.eclipse.jetty.util.log.class" system property can be used
+ * to select a specific logging implementation.
+ * <p>
+ * If the system property org.eclipse.jetty.util.log.IGNORED is set,
+ * then ignored exceptions are logged in detail.
+ *
+ * @see StdErrLog
+ * @see Slf4jLog
+ */
+public class Log
+{
+    public final static String EXCEPTION= "EXCEPTION ";
+    public final static String IGNORED= "IGNORED ";
+
+    /**
+     * Logging Configuration Properties
+     */
+    protected static Properties __props;
+    /**
+     * The {@link Logger} implementation class name
+     */
+    public static String __logClass;
+    /**
+     * Legacy flag indicating if {@link Log#ignore(Throwable)} methods produce any output in the {@link Logger}s
+     */
+    public static boolean __ignored;
+
+    /**
+     * Hold loggers only.
+     */
+    private final static ConcurrentMap<String, Logger> __loggers = new ConcurrentHashMap<String, Logger>();
+
+
+    static
+    {
+        /* Instantiate a default configuration properties (empty)
+         */
+        __props = new Properties();
+
+        AccessController.doPrivileged(new PrivilegedAction<Object>()
+        {
+            public Object run()
+            {
+                /* First see if the jetty-logging.properties object exists in the classpath.
+                 * This is an optional feature used by embedded mode use, and test cases to allow for early
+                 * configuration of the Log class in situations where access to the System.properties are
+                 * either too late or just impossible.
+                 */
+                URL testProps = Loader.getResource(Log.class,"jetty-logging.properties",true);
+                if (testProps != null)
+                {
+                    InputStream in = null;
+                    try
+                    {
+                        in = testProps.openStream();
+                        __props.load(in);
+                    }
+                    catch (IOException e)
+                    {
+                        System.err.println("Unable to load " + testProps);
+                        e.printStackTrace(System.err);
+                    }
+                    finally
+                    {
+                        IO.close(in);
+                    }
+                }
+
+                /* Now load the System.properties as-is into the __props, these values will override
+                 * any key conflicts in __props.
+                 */
+                @SuppressWarnings("unchecked")
+                Enumeration<String> systemKeyEnum = (Enumeration<String>)System.getProperties().propertyNames();
+                while (systemKeyEnum.hasMoreElements())
+                {
+                    String key = systemKeyEnum.nextElement();
+                    String val = System.getProperty(key);
+                    //protect against application code insertion of non-String values (returned as null)
+                    if (val != null)
+                        __props.setProperty(key,val);
+                }
+
+                /* Now use the configuration properties to configure the Log statics
+                 */
+                __logClass = __props.getProperty("org.eclipse.jetty.util.log.class","org.eclipse.jetty.util.log.Slf4jLog");
+                __ignored = Boolean.parseBoolean(__props.getProperty("org.eclipse.jetty.util.log.IGNORED","false"));
+                return null;
+            }
+        });
+    }
+
+    private static Logger LOG;
+    private static boolean __initialized;
+
+    public static boolean initialized()
+    {
+        if (LOG != null)
+        {
+            return true;
+        }
+
+        synchronized (Log.class)
+        {
+            if (__initialized)
+            {
+                return LOG != null;
+            }
+            __initialized = true;
+        }
+
+        try
+        {
+            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);
+        }
+
+        return LOG != null;
+    }
+
+    private static void initStandardLogging(Throwable e)
+    {
+        Class<?> log_class;
+        if(e != null && __ignored)
+        {
+            e.printStackTrace();
+        }
+
+        if (LOG == null)
+        {
+            log_class = StdErrLog.class;
+            LOG = new StdErrLog();
+            LOG.debug("Logging to {} via {}", LOG, log_class.getName());
+        }
+    }
+
+    public static void setLog(Logger log)
+    {
+        Log.LOG = log;
+    }
+
+    /**
+     * @deprecated anonymous logging is deprecated, use a named {@link Logger} obtained from {@link #getLogger(String)}
+     */
+    @Deprecated
+    public static Logger getLog()
+    {
+        initialized();
+        return LOG;
+    }
+
+    /**
+     * Get the root logger.
+     * @return the root logger
+     */
+    public static Logger getRootLogger() {
+        initialized();
+        return LOG;
+    }
+
+    static boolean isIgnored()
+    {
+        return __ignored;
+    }
+
+    /**
+     * Set Log to parent Logger.
+     * <p>
+     * If there is a different Log class available from a parent classloader,
+     * call {@link #getLogger(String)} on it and construct a {@link LoggerLog} instance
+     * as this Log's Logger, so that logging is delegated to the parent Log.
+     * <p>
+     * This should be used if a webapp is using Log, but wishes the logging to be
+     * directed to the containers log.
+     * <p>
+     * If there is not parent Log, then this call is equivalent to<pre>
+     *   Log.setLog(Log.getLogger(name));
+     * </pre>
+     * @param name Logger name
+     */
+    public static void setLogToParent(String name)
+    {
+        ClassLoader loader = Log.class.getClassLoader();
+        if (loader!=null && loader.getParent()!=null)
+        {
+            try
+            {
+                Class<?> uberlog = loader.getParent().loadClass("org.eclipse.jetty.util.log.Log");
+                Method getLogger = uberlog.getMethod("getLogger", new Class[]{String.class});
+                Object logger = getLogger.invoke(null,name);
+                setLog(new LoggerLog(logger));
+            }
+            catch (Exception e)
+            {
+                e.printStackTrace();
+            }
+        }
+        else
+        {
+            setLog(getLogger(name));
+        }
+    }
+
+    /**
+     * @deprecated anonymous logging is deprecated, use a named {@link Logger} obtained from {@link #getLogger(String)}
+     */
+    @Deprecated
+    public static void debug(Throwable th)
+    {
+        if (!isDebugEnabled())
+            return;
+        LOG.debug(EXCEPTION, th);
+    }
+
+    /**
+     * @deprecated anonymous logging is deprecated, use a named {@link Logger} obtained from {@link #getLogger(String)}
+     */
+    @Deprecated
+    public static void debug(String msg)
+    {
+        if (!initialized())
+            return;
+        LOG.debug(msg);
+    }
+
+    /**
+     * @deprecated anonymous logging is deprecated, use a named {@link Logger} obtained from {@link #getLogger(String)}
+     */
+    @Deprecated
+    public static void debug(String msg, Object arg)
+    {
+        if (!initialized())
+            return;
+        LOG.debug(msg, arg);
+    }
+
+    /**
+     * @deprecated anonymous logging is deprecated, use a named {@link Logger} obtained from {@link #getLogger(String)}
+     */
+    @Deprecated
+    public static void debug(String msg, Object arg0, Object arg1)
+    {
+        if (!initialized())
+            return;
+        LOG.debug(msg, arg0, arg1);
+    }
+
+    /**
+     * Ignore an exception unless trace is enabled.
+     * This works around the problem that log4j does not support the trace level.
+     * @param thrown the Throwable to ignore
+     */
+    /**
+     * @deprecated anonymous logging is deprecated, use a named {@link Logger} obtained from {@link #getLogger(String)}
+     */
+    @Deprecated
+    public static void ignore(Throwable thrown)
+    {
+        if (!initialized())
+            return;
+        LOG.ignore(thrown);
+    }
+
+    /**
+     * @deprecated anonymous logging is deprecated, use a named {@link Logger} obtained from {@link #getLogger(String)}
+     */
+    @Deprecated
+    public static void info(String msg)
+    {
+        if (!initialized())
+            return;
+        LOG.info(msg);
+    }
+
+    /**
+     * @deprecated anonymous logging is deprecated, use a named {@link Logger} obtained from {@link #getLogger(String)}
+     */
+    @Deprecated
+    public static void info(String msg, Object arg)
+    {
+        if (!initialized())
+            return;
+        LOG.info(msg, arg);
+    }
+
+    /**
+     * @deprecated anonymous logging is deprecated, use a named {@link Logger} obtained from {@link #getLogger(String)}
+     */
+    @Deprecated
+    public static void info(String msg, Object arg0, Object arg1)
+    {
+        if (!initialized())
+            return;
+        LOG.info(msg, arg0, arg1);
+    }
+
+    /**
+     * @deprecated anonymous logging is deprecated, use a named {@link Logger} obtained from {@link #getLogger(String)}
+     */
+    @Deprecated
+    public static boolean isDebugEnabled()
+    {
+        if (!initialized())
+            return false;
+        return LOG.isDebugEnabled();
+    }
+
+    /**
+     * @deprecated anonymous logging is deprecated, use a named {@link Logger} obtained from {@link #getLogger(String)}
+     */
+    @Deprecated
+    public static void warn(String msg)
+    {
+        if (!initialized())
+            return;
+        LOG.warn(msg);
+    }
+
+    /**
+     * @deprecated anonymous logging is deprecated, use a named {@link Logger} obtained from {@link #getLogger(String)}
+     */
+    @Deprecated
+    public static void warn(String msg, Object arg)
+    {
+        if (!initialized())
+            return;
+        LOG.warn(msg, arg);
+    }
+
+    /**
+     * @deprecated anonymous logging is deprecated, use a named {@link Logger} obtained from {@link #getLogger(String)}
+     */
+    @Deprecated
+    public static void warn(String msg, Object arg0, Object arg1)
+    {
+        if (!initialized())
+            return;
+        LOG.warn(msg, arg0, arg1);
+    }
+
+    /**
+     * @deprecated anonymous logging is deprecated, use a named {@link Logger} obtained from {@link #getLogger(String)}
+     */
+    @Deprecated
+    public static void warn(String msg, Throwable th)
+    {
+        if (!initialized())
+            return;
+        LOG.warn(msg, th);
+    }
+
+    /**
+     * @deprecated anonymous logging is deprecated, use a named {@link Logger} obtained from {@link #getLogger(String)}
+     */
+    @Deprecated
+    public static void warn(Throwable th)
+    {
+        if (!initialized())
+            return;
+        LOG.warn(EXCEPTION, th);
+    }
+
+    /**
+     * Obtain a named Logger based on the fully qualified class name.
+     *
+     * @param clazz
+     *            the class to base the Logger name off of
+     * @return the Logger with the given name
+     */
+    public static Logger getLogger(Class<?> clazz)
+    {
+        return getLogger(clazz.getName());
+    }
+
+    /**
+     * Obtain a named Logger or the default Logger if null is passed.
+     * @param name the Logger name
+     * @return the Logger with the given name
+     */
+    public static Logger getLogger(String name)
+    {
+        if (!initialized())
+            return null;
+
+        if(name==null)
+            return LOG;
+
+        Logger logger = __loggers.get(name);
+        if(logger==null)
+            logger = LOG.getLogger(name);
+
+        return logger;
+    }
+
+    static ConcurrentMap<String, Logger> getMutableLoggers()
+    {
+        return __loggers;
+    }
+    
+    /**
+     * Get a map of all configured {@link Logger} instances.
+     *
+     * @return a map of all configured {@link Logger} instances
+     */
+    public static Map<String, Logger> getLoggers()
+    {
+        return Collections.unmodifiableMap(__loggers);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/log/Logger.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,113 @@
+//
+//  ========================================================================
+//  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.util.log;
+
+/**
+ * A simple logging facade that is intended simply to capture the style of logging as used by Jetty.
+ */
+public interface Logger
+{
+    /**
+     * @return the name of this logger
+     */
+    public String getName();
+
+    /**
+     * Formats and logs at warn level.
+     * @param msg the formatting string
+     * @param args the optional arguments
+     */
+    public void warn(String msg, Object... args);
+
+    /**
+     * Logs the given Throwable information at warn level
+     * @param thrown the Throwable to log
+     */
+    public void warn(Throwable thrown);
+
+    /**
+     * Logs the given message at warn level, with Throwable information.
+     * @param msg the message to log
+     * @param thrown the Throwable to log
+     */
+    public void warn(String msg, Throwable thrown);
+
+    /**
+     * Formats and logs at info level.
+     * @param msg the formatting string
+     * @param args the optional arguments
+     */
+    public void info(String msg, Object... args);
+
+    /**
+     * Logs the given Throwable information at info level
+     * @param thrown the Throwable to log
+     */
+    public void info(Throwable thrown);
+
+    /**
+     * Logs the given message at info level, with Throwable information.
+     * @param msg the message to log
+     * @param thrown the Throwable to log
+     */
+    public void info(String msg, Throwable thrown);
+
+    /**
+     * @return whether the debug level is enabled
+     */
+    public boolean isDebugEnabled();
+
+    /**
+     * Mutator used to turn debug on programmatically.
+     * @param enabled whether to enable the debug level
+     */
+    public void setDebugEnabled(boolean enabled);
+
+    /**
+     * Formats and logs at debug level.
+     * @param msg the formatting string
+     * @param args the optional arguments
+     */
+    public void debug(String msg, Object... args);
+
+    /**
+     * Logs the given Throwable information at debug level
+     * @param thrown the Throwable to log
+     */
+    public void debug(Throwable thrown);
+
+    /**
+     * Logs the given message at debug level, with Throwable information.
+     * @param msg the message to log
+     * @param thrown the Throwable to log
+     */
+    public void debug(String msg, Throwable thrown);
+
+    /**
+     * @param name the name of the logger
+     * @return a logger with the given name
+     */
+    public Logger getLogger(String name);
+    
+    /**
+     * Ignore an exception.
+     * <p>This should be used rather than an empty catch block.
+     */
+    public void ignore(Throwable ignored); 
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/log/LoggerLog.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,213 @@
+//
+//  ========================================================================
+//  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.util.log;
+
+import java.lang.reflect.Method;
+
+/**
+ *
+ */
+public class LoggerLog extends AbstractLogger
+{
+    private final Object _logger;
+    private final Method _debugMT;
+    private final Method _debugMAA;
+    private final Method _infoMT;
+    private final Method _infoMAA;
+    private final Method _warnMT;
+    private final Method _warnMAA;
+    private final Method _setDebugEnabledE;
+    private final Method _getLoggerN;
+    private final Method _getName;
+    private volatile boolean _debug;
+
+    public LoggerLog(Object logger)
+    {
+        try
+        {
+            _logger = logger;
+            Class<?> lc = logger.getClass();
+            _debugMT = lc.getMethod("debug", new Class[]{String.class, Throwable.class});
+            _debugMAA = lc.getMethod("debug", new Class[]{String.class, Object[].class});
+            _infoMT = lc.getMethod("info", new Class[]{String.class, Throwable.class});
+            _infoMAA = lc.getMethod("info", new Class[]{String.class, Object[].class});
+            _warnMT = lc.getMethod("warn", new Class[]{String.class, Throwable.class});
+            _warnMAA = lc.getMethod("warn", new Class[]{String.class, Object[].class});
+            Method _isDebugEnabled = lc.getMethod("isDebugEnabled");
+            _setDebugEnabledE = lc.getMethod("setDebugEnabled", new Class[]{Boolean.TYPE});
+            _getLoggerN = lc.getMethod("getLogger", new Class[]{String.class});
+            _getName = lc.getMethod("getName");
+
+            _debug = (Boolean)_isDebugEnabled.invoke(_logger);
+        }
+        catch(Exception x)
+        {
+            throw new IllegalStateException(x);
+        }
+    }
+
+    public String getName()
+    {
+        try
+        {
+            return (String)_getName.invoke(_logger);
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    public void warn(String msg, Object... args)
+    {
+        try
+        {
+            _warnMAA.invoke(_logger, args);
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    public void warn(Throwable thrown)
+    {
+        warn("", thrown);
+    }
+
+    public void warn(String msg, Throwable thrown)
+    {
+        try
+        {
+            _warnMT.invoke(_logger, msg, thrown);
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    public void info(String msg, Object... args)
+    {
+        try
+        {
+            _infoMAA.invoke(_logger, args);
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    public void info(Throwable thrown)
+    {
+        info("", thrown);
+    }
+
+    public void info(String msg, Throwable thrown)
+    {
+        try
+        {
+            _infoMT.invoke(_logger, msg, thrown);
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    public boolean isDebugEnabled()
+    {
+        return _debug;
+    }
+
+    public void setDebugEnabled(boolean enabled)
+    {
+        try
+        {
+            _setDebugEnabledE.invoke(_logger, enabled);
+            _debug = enabled;
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    public void debug(String msg, Object... args)
+    {
+        if (!_debug)
+            return;
+
+        try
+        {
+            _debugMAA.invoke(_logger, args);
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    public void debug(Throwable thrown)
+    {
+        debug("", thrown);
+    }
+
+    public void debug(String msg, Throwable th)
+    {
+        if (!_debug)
+            return;
+
+        try
+        {
+            _debugMT.invoke(_logger, msg, th);
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    public void ignore(Throwable ignored)
+    {
+        if (Log.isIgnored())
+        {
+            warn(Log.IGNORED, ignored);
+        }
+    }
+
+    /**
+     * Create a Child Logger of this Logger.
+     */
+    protected Logger newLogger(String fullname)
+    {
+        try
+        {
+            Object logger=_getLoggerN.invoke(_logger, fullname);
+            return new LoggerLog(logger);
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+            return this;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/log/Slf4jLog.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,133 @@
+//
+//  ========================================================================
+//  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.util.log;
+
+
+
+/**
+ * Slf4jLog Logger
+ */
+public class Slf4jLog extends AbstractLogger
+{
+    private final org.slf4j.Logger _logger;
+
+    public Slf4jLog() throws Exception
+    {
+        this("org.eclipse.jetty.util.log");
+    }
+
+    public Slf4jLog(String name)
+    {
+        //NOTE: if only an slf4j-api jar is on the classpath, slf4j will use a NOPLogger
+        org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger( name );
+        
+        // Fix LocationAwareLogger use to indicate FQCN of this class - 
+        // https://bugs.eclipse.org/bugs/show_bug.cgi?id=276670
+        if (logger instanceof org.slf4j.spi.LocationAwareLogger)
+        {
+            _logger = new JettyAwareLogger((org.slf4j.spi.LocationAwareLogger)logger);
+        }
+        else
+        {
+            _logger = logger;
+        }
+    }
+
+    public String getName()
+    {
+        return _logger.getName();
+    }
+
+    public void warn(String msg, Object... args)
+    {
+        _logger.warn(msg, args);
+    }
+
+    public void warn(Throwable thrown)
+    {
+        warn("", thrown);
+    }
+
+    public void warn(String msg, Throwable thrown)
+    {
+        _logger.warn(msg, thrown);
+    }
+
+    public void info(String msg, Object... args)
+    {
+        _logger.info(msg, args);
+    }
+
+    public void info(Throwable thrown)
+    {
+        info("", thrown);
+    }
+
+    public void info(String msg, Throwable thrown)
+    {
+        _logger.info(msg, thrown);
+    }
+
+    public void debug(String msg, Object... args)
+    {
+        _logger.debug(msg, args);
+    }
+
+    public void debug(Throwable thrown)
+    {
+        debug("", thrown);
+    }
+
+    public void debug(String msg, Throwable thrown)
+    {
+        _logger.debug(msg, thrown);
+    }
+
+    public boolean isDebugEnabled()
+    {
+        return _logger.isDebugEnabled();
+    }
+
+    public void setDebugEnabled(boolean enabled)
+    {
+        warn("setDebugEnabled not implemented",null,null);
+    }
+
+    /**
+     * Create a Child Logger of this Logger.
+     */
+    protected Logger newLogger(String fullname)
+    {
+        return new Slf4jLog(fullname);
+    }
+
+    public void ignore(Throwable ignored)
+    {
+        if (Log.isIgnored())
+        {
+            warn(Log.IGNORED, ignored);
+        }
+    }
+
+    @Override
+    public String toString()
+    {
+        return _logger.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/log/StdErrLog.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,630 @@
+//
+//  ========================================================================
+//  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.util.log;
+
+import java.io.PrintStream;
+import java.security.AccessControlException;
+import java.util.Properties;
+
+import org.eclipse.jetty.util.DateCache;
+
+/**
+ * StdErr Logging. This implementation of the Logging facade sends all logs to StdErr with minimal formatting.
+ * <p>
+ * If the system property "org.eclipse.jetty.LEVEL" is set to one of the following (ALL, DEBUG, INFO, WARN), then set
+ * the eclipse jetty root level logger level to that specified level. (Default level is INFO)
+ * <p>
+ * If the system property "org.eclipse.jetty.util.log.SOURCE" is set, then the source method/file of a log is logged.
+ * For named debuggers, the system property name+".SOURCE" is checked, eg "org.eclipse.jetty.util.log.stderr.SOURCE". 
+ * If it is not not set, then "org.eclipse.jetty.util.log.SOURCE" is used as the default.
+ * <p>
+ * If the system property "org.eclipse.jetty.util.log.stderr.LONG" is set, then the full, unabbreviated name of the logger is
+ * used for logging.
+ */
+public class StdErrLog extends AbstractLogger
+{
+    private static final String EOL = System.getProperty("line.separator");
+    private static DateCache _dateCache;
+    private static final Properties __props = new Properties();
+
+    private final static boolean __source = Boolean.parseBoolean(Log.__props.getProperty("org.eclipse.jetty.util.log.SOURCE",
+            Log.__props.getProperty("org.eclipse.jetty.util.log.stderr.SOURCE","false")));
+    private final static boolean __long = Boolean.parseBoolean(Log.__props.getProperty("org.eclipse.jetty.util.log.stderr.LONG","false"));
+
+    static
+    {
+        __props.putAll(Log.__props);
+        
+        String deprecatedProperties[] =
+        { "DEBUG", "org.eclipse.jetty.util.log.DEBUG", "org.eclipse.jetty.util.log.stderr.DEBUG" };
+
+        // Toss a message to users about deprecated system properties
+        for (String deprecatedProp : deprecatedProperties)
+        {
+            if (System.getProperty(deprecatedProp) != null)
+            {
+                System.err.printf("System Property [%s] has been deprecated! (Use org.eclipse.jetty.LEVEL=DEBUG instead)%n",deprecatedProp);
+            }
+        }
+
+        try
+        {
+            _dateCache = new DateCache("yyyy-MM-dd HH:mm:ss");
+        }
+        catch (Exception x)
+        {
+            x.printStackTrace(System.err);
+        }
+    }
+
+    public static final int LEVEL_ALL = 0;
+    public static final int LEVEL_DEBUG = 1;
+    public static final int LEVEL_INFO = 2;
+    public static final int LEVEL_WARN = 3;
+
+    private int _level = LEVEL_INFO;
+    // Level that this Logger was configured as (remembered in special case of .setDebugEnabled())
+    private int _configuredLevel;
+    private PrintStream _stderr = null;
+    private boolean _source = __source;
+    // Print the long form names, otherwise use abbreviated
+    private boolean _printLongNames = __long;
+    // The full log name, as provided by the system.
+    private final String _name;
+    // The abbreviated log name (used by default, unless _long is specified)
+    private final String _abbrevname;
+    private boolean _hideStacks = false;
+
+    public StdErrLog()
+    {
+        this(null);
+    }
+
+    public StdErrLog(String name)
+    {
+        this(name,__props);
+    }
+
+    public StdErrLog(String name, Properties props)
+    {
+        if (props!=null && props!=__props)
+            __props.putAll(props);
+        this._name = name == null?"":name;
+        this._abbrevname = condensePackageString(this._name);
+        this._level = getLoggingLevel(props,this._name);
+        this._configuredLevel = this._level;
+
+        try
+        {
+            _source = Boolean.parseBoolean(props.getProperty(_name + ".SOURCE",Boolean.toString(_source)));
+        }
+        catch (AccessControlException ace)
+        {
+            _source = __source;
+        }
+    }
+
+    /**
+     * Get the Logging Level for the provided log name. Using the FQCN first, then each package segment from longest to
+     * shortest.
+     *
+     * @param props
+     *            the properties to check
+     * @param name
+     *            the name to get log for
+     * @return the logging level
+     */
+    public static int getLoggingLevel(Properties props, final String name)
+    {
+        // Calculate the level this named logger should operate under.
+        // Checking with FQCN first, then each package segment from longest to shortest.
+        String nameSegment = name;
+
+        while ((nameSegment != null) && (nameSegment.length() > 0))
+        {
+            String levelStr = props.getProperty(nameSegment + ".LEVEL");
+            // System.err.printf("[StdErrLog.CONFIG] Checking for property [%s.LEVEL] = %s%n",nameSegment,levelStr);
+            int level = getLevelId(nameSegment + ".LEVEL",levelStr);
+            if (level != (-1))
+            {
+                return level;
+            }
+
+            // Trim and try again.
+            int idx = nameSegment.lastIndexOf('.');
+            if (idx >= 0)
+            {
+                nameSegment = nameSegment.substring(0,idx);
+            }
+            else
+            {
+                nameSegment = null;
+            }
+        }
+
+        // Default Logging Level
+        return getLevelId("log.LEVEL",props.getProperty("log.LEVEL","INFO"));
+    }
+
+    protected static int getLevelId(String levelSegment, String levelName)
+    {
+        if (levelName == null)
+        {
+            return -1;
+        }
+        String levelStr = levelName.trim();
+        if ("ALL".equalsIgnoreCase(levelStr))
+        {
+            return LEVEL_ALL;
+        }
+        else if ("DEBUG".equalsIgnoreCase(levelStr))
+        {
+            return LEVEL_DEBUG;
+        }
+        else if ("INFO".equalsIgnoreCase(levelStr))
+        {
+            return LEVEL_INFO;
+        }
+        else if ("WARN".equalsIgnoreCase(levelStr))
+        {
+            return LEVEL_WARN;
+        }
+
+        System.err.println("Unknown StdErrLog level [" + levelSegment + "]=[" + levelStr + "], expecting only [ALL, DEBUG, INFO, WARN] as values.");
+        return -1;
+    }
+
+    /**
+     * Condenses a classname by stripping down the package name to just the first character of each package name
+     * segment.Configured
+     * <p>
+     *
+     * <pre>
+     * Examples:
+     * "org.eclipse.jetty.test.FooTest"           = "oejt.FooTest"
+     * "org.eclipse.jetty.server.logging.LogTest" = "orjsl.LogTest"
+     * </pre>
+     *
+     * @param classname
+     *            the fully qualified class name
+     * @return the condensed name
+     */
+    protected static String condensePackageString(String classname)
+    {
+        String parts[] = classname.split("\\.");
+        StringBuilder dense = new StringBuilder();
+        for (int i = 0; i < (parts.length - 1); i++)
+        {
+            dense.append(parts[i].charAt(0));
+        }
+        if (dense.length() > 0)
+        {
+            dense.append('.');
+        }
+        dense.append(parts[parts.length - 1]);
+        return dense.toString();
+    }
+
+    public String getName()
+    {
+        return _name;
+    }
+
+    public void setPrintLongNames(boolean printLongNames)
+    {
+        this._printLongNames = printLongNames;
+    }
+
+    public boolean isPrintLongNames()
+    {
+        return this._printLongNames;
+    }
+
+    public boolean isHideStacks()
+    {
+        return _hideStacks;
+    }
+
+    public void setHideStacks(boolean hideStacks)
+    {
+        _hideStacks = hideStacks;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Is the source of a log, logged
+     *
+     * @return true if the class, method, file and line number of a log is logged.
+     */
+    public boolean isSource()
+    {
+        return _source;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set if a log source is logged.
+     *
+     * @param source
+     *            true if the class, method, file and line number of a log is logged.
+     */
+    public void setSource(boolean source)
+    {
+        _source = source;
+    }
+
+    public void warn(String msg, Object... args)
+    {
+        if (_level <= LEVEL_WARN)
+        {
+            StringBuilder buffer = new StringBuilder(64);
+            format(buffer,":WARN:",msg,args);
+            (_stderr==null?System.err:_stderr).println(buffer);
+        }
+    }
+
+    public void warn(Throwable thrown)
+    {
+        warn("",thrown);
+    }
+
+    public void warn(String msg, Throwable thrown)
+    {
+        if (_level <= LEVEL_WARN)
+        {
+            StringBuilder buffer = new StringBuilder(64);
+            format(buffer,":WARN:",msg,thrown);
+            (_stderr==null?System.err:_stderr).println(buffer);
+        }
+    }
+
+    public void info(String msg, Object... args)
+    {
+        if (_level <= LEVEL_INFO)
+        {
+            StringBuilder buffer = new StringBuilder(64);
+            format(buffer,":INFO:",msg,args);
+            (_stderr==null?System.err:_stderr).println(buffer);
+        }
+    }
+
+    public void info(Throwable thrown)
+    {
+        info("",thrown);
+    }
+
+    public void info(String msg, Throwable thrown)
+    {
+        if (_level <= LEVEL_INFO)
+        {
+            StringBuilder buffer = new StringBuilder(64);
+            format(buffer,":INFO:",msg,thrown);
+            (_stderr==null?System.err:_stderr).println(buffer);
+        }
+    }
+
+    public boolean isDebugEnabled()
+    {
+        return (_level <= LEVEL_DEBUG);
+    }
+
+    /**
+     * Legacy interface where a programmatic configuration of the logger level
+     * is done as a wholesale approach.
+     */
+    public void setDebugEnabled(boolean enabled)
+    {
+        if (enabled)
+        {
+            this._level = LEVEL_DEBUG;
+
+            for (Logger log : Log.getLoggers().values())
+            {                
+                if (log.getName().startsWith(getName()) && log instanceof StdErrLog)
+                    ((StdErrLog)log).setLevel(LEVEL_DEBUG);
+            }
+        }
+        else
+        {
+            this._level = this._configuredLevel;
+            
+            for (Logger log : Log.getLoggers().values())
+            {
+                if (log.getName().startsWith(getName()) && log instanceof StdErrLog)
+                    ((StdErrLog)log).setLevel(((StdErrLog)log)._configuredLevel);
+            }
+        }
+    }
+
+    public int getLevel()
+    {
+        return _level;
+    }
+
+    /**
+     * Set the level for this logger.
+     * <p>
+     * Available values ({@link StdErrLog#LEVEL_ALL}, {@link StdErrLog#LEVEL_DEBUG}, {@link StdErrLog#LEVEL_INFO},
+     * {@link StdErrLog#LEVEL_WARN})
+     *
+     * @param level
+     *            the level to set the logger to
+     */
+    public void setLevel(int level)
+    {
+        this._level = level;
+    }
+
+    public void setStdErrStream(PrintStream stream)
+    {
+        this._stderr = stream==System.err?null:stream;
+    }
+
+    public void debug(String msg, Object... args)
+    {
+        if (_level <= LEVEL_DEBUG)
+        {
+            StringBuilder buffer = new StringBuilder(64);
+            format(buffer,":DBUG:",msg,args);
+            (_stderr==null?System.err:_stderr).println(buffer);
+        }
+    }
+
+    public void debug(Throwable thrown)
+    {
+        debug("",thrown);
+    }
+
+    public void debug(String msg, Throwable thrown)
+    {
+        if (_level <= LEVEL_DEBUG)
+        {
+            StringBuilder buffer = new StringBuilder(64);
+            format(buffer,":DBUG:",msg,thrown);
+            (_stderr==null?System.err:_stderr).println(buffer);
+        }
+    }
+
+    private void format(StringBuilder buffer, String level, String msg, Object... args)
+    {
+        String d = _dateCache.now();
+        int ms = _dateCache.lastMs();
+        tag(buffer,d,ms,level);
+        format(buffer,msg,args);
+    }
+
+    private void format(StringBuilder buffer, String level, String msg, Throwable thrown)
+    {
+        format(buffer,level,msg);
+        if (isHideStacks())
+        {
+            format(buffer,String.valueOf(thrown));
+        }
+        else
+        {
+            format(buffer,thrown);
+        }
+    }
+
+    private void tag(StringBuilder buffer, String d, int ms, String tag)
+    {
+        buffer.setLength(0);
+        buffer.append(d);
+        if (ms > 99)
+        {
+            buffer.append('.');
+        }
+        else if (ms > 9)
+        {
+            buffer.append(".0");
+        }
+        else
+        {
+            buffer.append(".00");
+        }
+        buffer.append(ms).append(tag);
+        if (_printLongNames)
+        {
+            buffer.append(_name);
+        }
+        else
+        {
+            buffer.append(_abbrevname);
+        }
+        buffer.append(':');
+        if (_source)
+        {
+            Throwable source = new Throwable();
+            StackTraceElement[] frames = source.getStackTrace();
+            for (int i = 0; i < frames.length; i++)
+            {
+                final StackTraceElement frame = frames[i];
+                String clazz = frame.getClassName();
+                if (clazz.equals(StdErrLog.class.getName()) || clazz.equals(Log.class.getName()))
+                {
+                    continue;
+                }
+                if (!_printLongNames && clazz.startsWith("org.eclipse.jetty."))
+                {
+                    buffer.append(condensePackageString(clazz));
+                }
+                else
+                {
+                    buffer.append(clazz);
+                }
+                buffer.append('#').append(frame.getMethodName());
+                if (frame.getFileName() != null)
+                {
+                    buffer.append('(').append(frame.getFileName()).append(':').append(frame.getLineNumber()).append(')');
+                }
+                buffer.append(':');
+                break;
+            }
+        }
+    }
+
+    private void format(StringBuilder builder, String msg, Object... args)
+    {
+        if (msg == null)
+        {
+            msg = "";
+            for (int i = 0; i < args.length; i++)
+            {
+                msg += "{} ";
+            }
+        }
+        String braces = "{}";
+        int start = 0;
+        for (Object arg : args)
+        {
+            int bracesIndex = msg.indexOf(braces,start);
+            if (bracesIndex < 0)
+            {
+                escape(builder,msg.substring(start));
+                builder.append(" ");
+                builder.append(arg);
+                start = msg.length();
+            }
+            else
+            {
+                escape(builder,msg.substring(start,bracesIndex));
+                builder.append(String.valueOf(arg));
+                start = bracesIndex + braces.length();
+            }
+        }
+        escape(builder,msg.substring(start));
+    }
+
+    private void escape(StringBuilder builder, String string)
+    {
+        for (int i = 0; i < string.length(); ++i)
+        {
+            char c = string.charAt(i);
+            if (Character.isISOControl(c))
+            {
+                if (c == '\n')
+                {
+                    builder.append('|');
+                }
+                else if (c == '\r')
+                {
+                    builder.append('<');
+                }
+                else
+                {
+                    builder.append('?');
+                }
+            }
+            else
+            {
+                builder.append(c);
+            }
+        }
+    }
+
+    private void format(StringBuilder buffer, Throwable thrown)
+    {
+        if (thrown == null)
+        {
+            buffer.append("null");
+        }
+        else
+        {
+            buffer.append(EOL);
+            format(buffer,thrown.toString());
+            StackTraceElement[] elements = thrown.getStackTrace();
+            for (int i = 0; elements != null && i < elements.length; i++)
+            {
+                buffer.append(EOL).append("\tat ");
+                format(buffer,elements[i].toString());
+            }
+
+            Throwable cause = thrown.getCause();
+            if (cause != null && cause != thrown)
+            {
+                buffer.append(EOL).append("Caused by: ");
+                format(buffer,cause);
+            }
+        }
+    }
+
+
+    /**
+     * Create a Child Logger of this Logger.
+     */
+    protected Logger newLogger(String fullname)
+    {
+        StdErrLog logger = new StdErrLog(fullname);
+        // Preserve configuration for new loggers configuration
+        logger.setPrintLongNames(_printLongNames);
+        // Let Level come from configured Properties instead - sel.setLevel(_level);
+        logger.setSource(_source);
+        logger._stderr = this._stderr;
+        
+        // Force the child to have any programmatic configuration
+        if (_level!=_configuredLevel)
+            logger._level=_level;
+
+        return logger;
+    }
+
+    @Override
+    public String toString()
+    {
+        StringBuilder s = new StringBuilder();
+        s.append("StdErrLog:");
+        s.append(_name);
+        s.append(":LEVEL=");
+        switch (_level)
+        {
+            case LEVEL_ALL:
+                s.append("ALL");
+                break;
+            case LEVEL_DEBUG:
+                s.append("DEBUG");
+                break;
+            case LEVEL_INFO:
+                s.append("INFO");
+                break;
+            case LEVEL_WARN:
+                s.append("WARN");
+                break;
+            default:
+                s.append("?");
+                break;
+        }
+        return s.toString();
+    }
+
+    public static void setProperties(Properties props)
+    {
+        __props.clear();
+        __props.putAll(props);
+    }
+
+    public void ignore(Throwable ignored)
+    {
+        if (_level <= LEVEL_ALL)
+        {
+            StringBuilder buffer = new StringBuilder(64);
+            format(buffer,":IGNORED:","",ignored);
+            (_stderr==null?System.err:_stderr).println(buffer);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/preventers/AWTLeakPreventer.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,47 @@
+//
+//  ========================================================================
+//  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.util.preventers;
+
+import java.awt.Toolkit;
+
+/**
+ * AWTLeakPreventer
+ *
+ * See https://issues.jboss.org/browse/AS7-3733
+ * 
+ * The java.awt.Toolkit class has a static field that is the default toolkit. 
+ * Creating the default toolkit causes the creation of an EventQueue, which has a 
+ * classloader field initialized by the thread context class loader. 
+ *
+ */
+public class AWTLeakPreventer extends AbstractLeakPreventer
+{
+   
+    /* ------------------------------------------------------------ */
+    /** 
+     * @see org.eclipse.jetty.util.preventers.AbstractLeakPreventer#prevent(java.lang.ClassLoader)
+     */
+    @Override
+    public void prevent(ClassLoader loader)
+    {
+        LOG.debug("Pinning classloader for java.awt.EventQueue using "+loader);
+        Toolkit.getDefaultToolkit();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/preventers/AbstractLeakPreventer.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,62 @@
+//
+//  ========================================================================
+//  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.util.preventers;
+
+import org.eclipse.jetty.util.component.AbstractLifeCycle;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * AbstractLeakPreventer
+ *
+ * Abstract base class for code that seeks to avoid pinning of webapp classloaders by using the jetty classloader to
+ * proactively call the code that pins them (generally pinned as static data members, or as static
+ * data members that are daemon threads (which use the context classloader)).
+ * 
+ * Instances of subclasses of this class should be set with Server.addBean(), which will
+ * ensure that they are called when the Server instance starts up, which will have the jetty
+ * classloader in scope.
+ *
+ */
+public abstract class AbstractLeakPreventer extends AbstractLifeCycle
+{
+    protected static final Logger LOG = Log.getLogger(AbstractLeakPreventer.class);
+    
+    /* ------------------------------------------------------------ */
+    abstract public void prevent(ClassLoader loader);
+    
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    protected void doStart() throws Exception
+    {
+        ClassLoader loader = Thread.currentThread().getContextClassLoader();
+        try
+        {
+            Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
+            prevent(getClass().getClassLoader());
+            super.doStart();
+        }
+        finally
+        {
+            Thread.currentThread().setContextClassLoader(loader);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/preventers/AppContextLeakPreventer.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,41 @@
+//
+//  ========================================================================
+//  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.util.preventers;
+
+import javax.imageio.ImageIO;
+
+/**
+ * AppContextLeakPreventer
+ *
+ * Cause the classloader that is pinned by AppContext.getAppContext() to be 
+ * a container or system classloader, not a webapp classloader.
+ * 
+ * Inspired by Tomcat JreMemoryLeakPrevention.
+ */
+public class AppContextLeakPreventer extends AbstractLeakPreventer
+{
+    /* ------------------------------------------------------------ */
+    @Override
+    public void prevent(ClassLoader loader)
+    {
+        LOG.debug("Pinning classloader for AppContext.getContext() with "+loader);
+        ImageIO.getUseCache();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/preventers/DOMLeakPreventer.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,56 @@
+//
+//  ========================================================================
+//  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.util.preventers;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+
+/**
+ * DOMLeakPreventer
+ *
+ * See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6916498
+ * 
+ * Prevent the RuntimeException that is a static member of AbstractDOMParser
+ * from pinning a webapp classloader by causing it to be set here by a non-webapp classloader.
+ * 
+ * Note that according to the bug report, a heap dump may not identify the GCRoot, making 
+ * it difficult to identify the cause of the leak.
+ *
+ */
+public class DOMLeakPreventer extends AbstractLeakPreventer
+{
+    /* ------------------------------------------------------------ */
+    /** 
+     * @see org.eclipse.jetty.util.preventers.AbstractLeakPreventer#prevent(java.lang.ClassLoader)
+     */
+    @Override
+    public void prevent(ClassLoader loader)
+    {
+        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        try 
+        {
+            factory.newDocumentBuilder();
+        } 
+        catch (Exception e) 
+        {
+            LOG.warn(e);
+        }
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/preventers/DriverManagerLeakPreventer.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,42 @@
+//
+//  ========================================================================
+//  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.util.preventers;
+
+import java.sql.DriverManager;
+
+
+/**
+ * DriverManagerLeakPreventer
+ *
+ * Cause DriverManager.getCallerClassLoader() to be called, which will pin the classloader.
+ * 
+ * Inspired by Tomcat JreMemoryLeakPrevention.
+ */
+public class DriverManagerLeakPreventer extends AbstractLeakPreventer
+{
+    /* ------------------------------------------------------------ */
+    @Override
+    public void prevent(ClassLoader loader)
+    {
+        LOG.debug("Pinning DriverManager classloader with "+loader);
+        DriverManager.getDrivers();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/preventers/GCThreadLeakPreventer.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,64 @@
+//
+//  ========================================================================
+//  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.util.preventers;
+
+import java.lang.reflect.Method;
+
+/**
+ * GCThreadLeakPreventer
+ *
+ * Prevents a call to sun.misc.GC.requestLatency pinning a webapp classloader
+ * by calling it with a non-webapp classloader. The problem appears to be that
+ * when this method is called, a daemon thread is created which takes the 
+ * context classloader. A known caller of this method is the RMI impl. See
+ * http://stackoverflow.com/questions/6626680/does-java-garbage-collection-log-entry-full-gc-system-mean-some-class-called
+ * 
+ * This preventer will start the thread with the longest possible interval, although
+ * subsequent calls can vary that. Recommend to only use this class if you're doing
+ * RMI.
+ * 
+ * Inspired by Tomcat JreMemoryLeakPrevention.
+ *
+ */
+public class GCThreadLeakPreventer extends AbstractLeakPreventer
+{
+    /* ------------------------------------------------------------ */
+    /** 
+     * @see org.eclipse.jetty.util.preventers.AbstractLeakPreventer#prevent(java.lang.ClassLoader)
+     */
+    @Override
+    public void prevent(ClassLoader loader)
+    {
+        try
+        {
+            Class clazz = Class.forName("sun.misc.GC");
+            Method requestLatency = clazz.getMethod("requestLatency", new Class[] {long.class});
+            requestLatency.invoke(null, Long.valueOf(Long.MAX_VALUE-1));
+        }
+        catch (ClassNotFoundException e)
+        {
+            LOG.ignore(e);
+        }
+        catch (Exception e)
+        {
+            LOG.warn(e);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/preventers/Java2DLeakPreventer.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,49 @@
+//
+//  ========================================================================
+//  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.util.preventers;
+
+/**
+ * Java2DLeakPreventer
+ *
+ * Prevent pinning of webapp classloader by pre-loading sun.java2d.Disposer class
+ * before webapp classloaders are created.
+ * 
+ * See https://issues.apache.org/bugzilla/show_bug.cgi?id=51687
+ *
+ */
+public class Java2DLeakPreventer extends AbstractLeakPreventer
+{
+    /* ------------------------------------------------------------ */
+    /** 
+     * @see org.eclipse.jetty.util.preventers.AbstractLeakPreventer#prevent(java.lang.ClassLoader)
+     */
+    @Override
+    public void prevent(ClassLoader loader)
+    {
+        try
+        {
+            Class.forName("sun.java2d.Disposer", true, loader);
+        }
+        catch (ClassNotFoundException e)
+        {
+            LOG.ignore(e);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/preventers/LDAPLeakPreventer.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,51 @@
+//
+//  ========================================================================
+//  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.util.preventers;
+
+/**
+ * LDAPLeakPreventer
+ *
+ * If com.sun.jndi.LdapPoolManager class is loaded and the system property
+ * com.sun.jndi.ldap.connect.pool.timeout is set to a nonzero value, a daemon
+ * thread is started which can pin a webapp classloader if it is the first to
+ * load the LdapPoolManager.
+ * 
+ * Inspired by Tomcat JreMemoryLeakPrevention
+ *
+ */
+public class LDAPLeakPreventer extends AbstractLeakPreventer
+{
+    /* ------------------------------------------------------------ */
+    /** 
+     * @see org.eclipse.jetty.util.preventers.AbstractLeakPreventer#prevent(java.lang.ClassLoader)
+     */
+    @Override
+    public void prevent(ClassLoader loader)
+    {
+        try
+        {
+            Class.forName("com.sun.jndi.LdapPoolManager", true, loader);
+        }
+        catch (ClassNotFoundException e)
+        {
+            LOG.ignore(e);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/preventers/LoginConfigurationLeakPreventer.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,49 @@
+//
+//  ========================================================================
+//  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.util.preventers;
+
+/**
+ * LoginConfigurationLeakPreventer
+ *
+ * The javax.security.auth.login.Configuration class keeps a static reference to the 
+ * thread context classloader. We prevent a webapp context classloader being used for
+ * that by invoking the classloading here.
+ * 
+ * Inspired by Tomcat JreMemoryLeakPrevention
+ */
+public class LoginConfigurationLeakPreventer extends AbstractLeakPreventer
+{
+    /* ------------------------------------------------------------ */
+    /** 
+     * @see org.eclipse.jetty.util.preventers.AbstractLeakPreventer#prevent(java.lang.ClassLoader)
+     */
+    @Override
+    public void prevent(ClassLoader loader)
+    {
+        try
+        {
+            Class.forName("javax.security.auth.login.Configuration", true, loader);
+        }
+        catch (ClassNotFoundException e)
+        {
+            LOG.warn(e);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/preventers/SecurityProviderLeakPreventer.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,44 @@
+//
+//  ========================================================================
+//  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.util.preventers;
+
+import java.security.Security;
+
+/**
+ * SecurityProviderLeakPreventer
+ *
+ * Some security providers, such as sun.security.pkcs11.SunPKCS11 start a deamon thread,
+ * which will use the thread context classloader. Load them here to ensure the classloader
+ * is not a webapp classloader.
+ *
+ * Inspired by Tomcat JreMemoryLeakPrevention
+ */
+public class SecurityProviderLeakPreventer extends AbstractLeakPreventer
+{
+    /* ------------------------------------------------------------ */
+    /** 
+     * @see org.eclipse.jetty.util.preventers.AbstractLeakPreventer#prevent(java.lang.ClassLoader)
+     */
+    @Override
+    public void prevent(ClassLoader loader)
+    {
+        Security.getProviders();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/resource/BadResource.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,139 @@
+//
+//  ========================================================================
+//  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.util.resource;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+
+
+/* ------------------------------------------------------------ */
+/** Bad Resource.
+ *
+ * A Resource that is returned for a bade URL.  Acts as a resource
+ * that does not exist and throws appropriate exceptions.
+ *
+ * 
+ */
+class BadResource extends URLResource
+{
+    /* ------------------------------------------------------------ */
+    private String _message=null;
+        
+    /* -------------------------------------------------------- */
+    BadResource(URL url,  String message)
+    {
+        super(url,null);
+        _message=message;
+    }
+    
+
+    /* -------------------------------------------------------- */
+    @Override
+    public boolean exists()
+    {
+        return false;
+    }
+        
+    /* -------------------------------------------------------- */
+    @Override
+    public long lastModified()
+    {
+        return -1;
+    }
+
+    /* -------------------------------------------------------- */
+    @Override
+    public boolean isDirectory()
+    {
+        return false;
+    }
+
+    /* --------------------------------------------------------- */
+    @Override
+    public long length()
+    {
+        return -1;
+    }
+        
+        
+    /* ------------------------------------------------------------ */
+    @Override
+    public File getFile()
+    {
+        return null;
+    }
+        
+    /* --------------------------------------------------------- */
+    @Override
+    public InputStream getInputStream() throws IOException
+    {
+        throw new FileNotFoundException(_message);
+    }
+        
+    /* --------------------------------------------------------- */
+    @Override
+    public OutputStream getOutputStream()
+        throws java.io.IOException, SecurityException
+    {
+        throw new FileNotFoundException(_message);
+    }
+        
+    /* --------------------------------------------------------- */
+    @Override
+    public boolean delete()
+        throws SecurityException
+    {
+        throw new SecurityException(_message);
+    }
+
+    /* --------------------------------------------------------- */
+    @Override
+    public boolean renameTo( Resource dest)
+        throws SecurityException
+    {
+        throw new SecurityException(_message);
+    }
+
+    /* --------------------------------------------------------- */
+    @Override
+    public String[] list()
+    {
+        return null;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void copyTo(File destination)
+        throws IOException
+    {
+        throw new SecurityException(_message);
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public String toString()
+    {
+        return super.toString()+"; BadResource="+_message;
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/resource/FileResource.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,400 @@
+//
+//  ========================================================================
+//  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.util.resource;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.Permission;
+
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.URIUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+
+/* ------------------------------------------------------------ */
+/** File Resource.
+ *
+ * Handle resources of implied or explicit file type.
+ * This class can check for aliasing in the filesystem (eg case
+ * insensitivity).  By default this is turned on, or it can be controlled 
+ * by calling the static method @see FileResource#setCheckAliases(boolean)
+ * 
+ */
+public class FileResource extends URLResource
+{
+    private static final Logger LOG = Log.getLogger(FileResource.class);
+    private static boolean __checkAliases = true;
+
+    /* ------------------------------------------------------------ */
+    private File _file;
+    private transient URL _alias=null;
+    private transient boolean _aliasChecked=false;
+
+    /* ------------------------------------------------------------------------------- */
+    /** setCheckAliases.
+     * @param checkAliases True of resource aliases are to be checked for (eg case insensitivity or 8.3 short names) and treated as not found.
+     */
+    public static void setCheckAliases(boolean checkAliases)
+    {
+        __checkAliases=checkAliases;
+    }
+
+    /* ------------------------------------------------------------------------------- */
+    /** getCheckAliases.
+     * @return True of resource aliases are to be checked for (eg case insensitivity or 8.3 short names) and treated as not found.
+     */
+    public static boolean getCheckAliases()
+    {
+        return __checkAliases;
+    }
+    
+    /* -------------------------------------------------------- */
+    public FileResource(URL url)
+        throws IOException, URISyntaxException
+    {
+        super(url,null);
+
+        try
+        {
+            // Try standard API to convert URL to file.
+            _file =new File(new URI(url.toString()));
+        }
+        catch (URISyntaxException e) 
+        {
+            throw e;
+        }
+        catch (Exception e)
+        {
+            LOG.ignore(e);
+            try
+            {
+                // Assume that File.toURL produced unencoded chars. So try
+                // encoding them.
+                String file_url="file:"+URIUtil.encodePath(url.toString().substring(5));           
+                URI uri = new URI(file_url);
+                if (uri.getAuthority()==null) 
+                    _file = new File(uri);
+                else
+                    _file = new File("//"+uri.getAuthority()+URIUtil.decodePath(url.getFile()));
+            }
+            catch (Exception e2)
+            {
+                LOG.ignore(e2);
+
+                // Still can't get the file.  Doh! try good old hack!
+                checkConnection();
+                Permission perm = _connection.getPermission();
+                _file = new File(perm==null?url.getFile():perm.getName());
+            }
+        }
+        if (_file.isDirectory())
+        {
+            if (!_urlString.endsWith("/"))
+                _urlString=_urlString+"/";
+        }
+        else
+        {
+            if (_urlString.endsWith("/"))
+                _urlString=_urlString.substring(0,_urlString.length()-1);
+        }
+
+    }
+
+    /* -------------------------------------------------------- */
+    FileResource(URL url, URLConnection connection, File file)
+    {
+        super(url,connection);
+        _file=file;
+        if (_file.isDirectory() && !_urlString.endsWith("/"))
+            _urlString=_urlString+"/";
+    }
+    
+    /* -------------------------------------------------------- */
+    @Override
+    public Resource addPath(String path)
+        throws IOException,MalformedURLException
+    {
+        URLResource r=null;
+        String url=null;
+
+        path = org.eclipse.jetty.util.URIUtil.canonicalPath(path);
+       
+        if ("/".equals(path))
+            return this;
+        else if (!isDirectory())
+        {
+            r=(FileResource)super.addPath(path);
+            url=r._urlString;
+        }
+        else
+        {
+            if (path==null)
+                throw new MalformedURLException();   
+            
+            // treat all paths being added as relative
+            String rel=path;
+            if (path.startsWith("/"))
+                rel = path.substring(1);
+            
+            url=URIUtil.addPaths(_urlString,URIUtil.encodePath(rel));
+            r=(URLResource)Resource.newResource(url);
+        }
+        
+        // Check for encoding aliases
+        // The encoded path should be a suffix of the resource (give or take a directory / )
+        String encoded=URIUtil.encodePath(path);
+        int expected=r.toString().length()-encoded.length();
+        int index = r._urlString.lastIndexOf(encoded, expected);
+        if (expected!=index && ((expected-1)!=index || path.endsWith("/") || !r.isDirectory()))
+        {
+            if (r instanceof FileResource)
+            {
+                ((FileResource)r)._alias=((FileResource)r)._file.getCanonicalFile().toURI().toURL();
+                ((FileResource)r)._aliasChecked=true;
+            }
+        }                             
+        return r;
+    }
+   
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public URL getAlias()
+    {
+        if (__checkAliases && !_aliasChecked)
+        {
+            try
+            {    
+                String abs=_file.getAbsolutePath();
+                String can=_file.getCanonicalPath();
+                
+                if (abs.length()!=can.length() || !abs.equals(can))
+                    _alias=Resource.toURL(new File(can));
+                
+                _aliasChecked=true;
+                
+                if (_alias!=null && LOG.isDebugEnabled())
+                {
+                    LOG.debug("ALIAS abs="+abs);
+                    LOG.debug("ALIAS can="+can);
+                }
+            }
+            catch(Exception e)
+            {
+                LOG.warn(Log.EXCEPTION,e);
+                return getURL();
+            }                
+        }
+        return _alias;
+    }
+    
+    /* -------------------------------------------------------- */
+    /**
+     * Returns true if the resource exists.
+     */
+    @Override
+    public boolean exists()
+    {
+        return _file.exists();
+    }
+        
+    /* -------------------------------------------------------- */
+    /**
+     * Returns the last modified time
+     */
+    @Override
+    public long lastModified()
+    {
+        return _file.lastModified();
+    }
+
+    /* -------------------------------------------------------- */
+    /**
+     * Returns true if the respresenetd resource is a container/directory.
+     */
+    @Override
+    public boolean isDirectory()
+    {
+        return _file.isDirectory();
+    }
+
+    /* --------------------------------------------------------- */
+    /**
+     * Return the length of the resource
+     */
+    @Override
+    public long length()
+    {
+        return _file.length();
+    }
+        
+
+    /* --------------------------------------------------------- */
+    /**
+     * Returns the name of the resource
+     */
+    @Override
+    public String getName()
+    {
+        return _file.getAbsolutePath();
+    }
+        
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns an File representing the given resource or NULL if this
+     * is not possible.
+     */
+    @Override
+    public File getFile()
+    {
+        return _file;
+    }
+        
+    /* --------------------------------------------------------- */
+    /**
+     * Returns an input stream to the resource
+     */
+    @Override
+    public InputStream getInputStream() throws IOException
+    {
+        return new FileInputStream(_file);
+    }
+        
+    /* --------------------------------------------------------- */
+    /**
+     * Returns an output stream to the resource
+     */
+    @Override
+    public OutputStream getOutputStream()
+        throws java.io.IOException, SecurityException
+    {
+        return new FileOutputStream(_file);
+    }
+        
+    /* --------------------------------------------------------- */
+    /**
+     * Deletes the given resource
+     */
+    @Override
+    public boolean delete()
+        throws SecurityException
+    {
+        return _file.delete();
+    }
+
+    /* --------------------------------------------------------- */
+    /**
+     * Rename the given resource
+     */
+    @Override
+    public boolean renameTo( Resource dest)
+        throws SecurityException
+    {
+        if( dest instanceof FileResource)
+            return _file.renameTo( ((FileResource)dest)._file);
+        else
+            return false;
+    }
+
+    /* --------------------------------------------------------- */
+    /**
+     * Returns a list of resources contained in the given resource
+     */
+    @Override
+    public String[] list()
+    {
+        String[] list =_file.list();
+        if (list==null)
+            return null;
+        for (int i=list.length;i-->0;)
+        {
+            if (new File(_file,list[i]).isDirectory() &&
+                !list[i].endsWith("/"))
+                list[i]+="/";
+        }
+        return list;
+    }
+         
+    /* ------------------------------------------------------------ */
+    /** Encode according to this resource type.
+     * File URIs are encoded.
+     * @param uri URI to encode.
+     * @return The uri unchanged.
+     */
+    @Override
+    public String encode(String uri)
+    {
+        return uri;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** 
+     * @param o
+     * @return <code>true</code> of the object <code>o</code> is a {@link FileResource} pointing to the same file as this resource. 
+     */
+    @Override
+    public boolean equals( Object o)
+    {
+        if (this == o)
+            return true;
+
+        if (null == o || ! (o instanceof FileResource))
+            return false;
+
+        FileResource f=(FileResource)o;
+        return f._file == _file || (null != _file && _file.equals(f._file));
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return the hashcode.
+     */
+    @Override
+    public int hashCode()
+    {
+       return null == _file ? super.hashCode() : _file.hashCode();
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public void copyTo(File destination)
+        throws IOException
+    {
+        if (isDirectory())
+        {
+            IO.copyDir(getFile(),destination);
+        }
+        else
+        {
+            if (destination.exists())
+                throw new IllegalArgumentException(destination+" exists");
+            IO.copy(getFile(),destination);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/resource/JarFileResource.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,435 @@
+//
+//  ========================================================================
+//  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.util.resource;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.JarURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/* ------------------------------------------------------------ */
+class JarFileResource extends JarResource
+{
+    private static final Logger LOG = Log.getLogger(JarFileResource.class);
+    private JarFile _jarFile;
+    private File _file;
+    private String[] _list;
+    private JarEntry _entry;
+    private boolean _directory;
+    private String _jarUrl;
+    private String _path;
+    private boolean _exists;
+    
+    /* -------------------------------------------------------- */
+    JarFileResource(URL url)
+    {
+        super(url);
+    }
+    
+    /* ------------------------------------------------------------ */
+    JarFileResource(URL url, boolean useCaches)
+    {
+        super(url, useCaches);
+    }
+   
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public synchronized void release()
+    {
+        _list=null;
+        _entry=null;
+        _file=null;
+        //if the jvm is not doing url caching, then the JarFiles will not be cached either,
+        //and so they are safe to close
+        if (!getUseCaches())
+        {
+            if ( _jarFile != null )
+            {
+                try
+                {
+                    LOG.debug("Closing JarFile "+_jarFile.getName());
+                    _jarFile.close();
+                }
+                catch ( IOException ioe )
+                {
+                    LOG.ignore(ioe);
+                }
+            }
+        }
+        _jarFile=null;
+        super.release();
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    protected boolean checkConnection()
+    {
+        try
+        {
+            super.checkConnection();
+        }
+        finally
+        {
+            if (_jarConnection==null)
+            {
+                _entry=null;
+                _file=null;
+                _jarFile=null;
+                _list=null;
+            }
+        }
+        return _jarFile!=null;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    @Override
+    protected synchronized void newConnection()
+        throws IOException
+    {
+        super.newConnection();
+        
+        _entry=null;
+        _file=null;
+        _jarFile=null;
+        _list=null;
+        
+        int sep = _urlString.indexOf("!/");
+        _jarUrl=_urlString.substring(0,sep+2);
+        _path=_urlString.substring(sep+2);
+        if (_path.length()==0)
+            _path=null;   
+        _jarFile=_jarConnection.getJarFile();
+        _file=new File(_jarFile.getName());
+    }
+    
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns true if the represented resource exists.
+     */
+    @Override
+    public boolean exists()
+    {
+        if (_exists)
+            return true;
+
+        if (_urlString.endsWith("!/"))
+        {
+            
+            String file_url=_urlString.substring(4,_urlString.length()-2);
+            try{return newResource(file_url).exists();}
+            catch(Exception e) {LOG.ignore(e); return false;}
+        }
+        
+        boolean check=checkConnection();
+        
+        // Is this a root URL?
+        if (_jarUrl!=null && _path==null)
+        {
+            // Then if it exists it is a directory
+            _directory=check;
+            return true;
+        }
+        else 
+        {
+            // Can we find a file for it?
+            JarFile jarFile=null;
+            if (check)
+                // Yes
+                jarFile=_jarFile;
+            else
+            {
+                // No - so lets look if the root entry exists.
+                try
+                {
+                    JarURLConnection c=(JarURLConnection)((new URL(_jarUrl)).openConnection());
+                    c.setUseCaches(getUseCaches());
+                    jarFile=c.getJarFile();
+                }
+                catch(Exception e)
+                {
+                       LOG.ignore(e);
+                }
+            }
+
+            // Do we need to look more closely?
+            if (jarFile!=null && _entry==null && !_directory)
+            {
+                // OK - we have a JarFile, lets look at the entries for our path
+                Enumeration<JarEntry> e=jarFile.entries();
+                while(e.hasMoreElements())
+                {
+                    JarEntry entry = (JarEntry) e.nextElement();
+                    String name=entry.getName().replace('\\','/');
+                    
+                    // Do we have a match
+                    if (name.equals(_path))
+                    {
+                        _entry=entry;
+                        // Is the match a directory
+                        _directory=_path.endsWith("/");
+                        break;
+                    }
+                    else if (_path.endsWith("/"))
+                    {
+                        if (name.startsWith(_path))
+                        {
+                            _directory=true;
+                            break;
+                        }
+                    }
+                    else if (name.startsWith(_path) && name.length()>_path.length() && name.charAt(_path.length())=='/')
+                    {
+                        _directory=true;
+                        break;
+                    }
+                }
+
+                if (_directory && !_urlString.endsWith("/"))
+                {
+                    _urlString+="/";
+                    try
+                    {
+                        _url=new URL(_urlString);
+                    }
+                    catch(MalformedURLException ex)
+                    {
+                        LOG.warn(ex);
+                    }
+                }
+            }
+        }    
+        
+        _exists= ( _directory || _entry!=null);
+        return _exists;
+    }
+
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns true if the represented resource is a container/directory.
+     * If the resource is not a file, resources ending with "/" are
+     * considered directories.
+     */
+    @Override
+    public boolean isDirectory()
+    {
+        return _urlString.endsWith("/") || exists() && _directory;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns the last modified time
+     */
+    @Override
+    public long lastModified()
+    {
+        if (checkConnection() && _file!=null)
+        {
+            if (exists() && _entry!=null)
+                return _entry.getTime();
+            return _file.lastModified();
+        }
+        return -1;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public synchronized String[] list()
+    {
+        if (isDirectory() && _list==null)
+        {
+            List<String> list = null;
+            try
+            {
+                list = listEntries();
+            }
+            catch (Exception e)
+            {
+                //Sun's JarURLConnection impl for jar: protocol will close a JarFile in its connect() method if
+                //useCaches == false (eg someone called URLConnection with defaultUseCaches==true).
+                //As their sun.net.www.protocol.jar package caches JarFiles and/or connections, we can wind up in 
+                //the situation where the JarFile we have remembered in our _jarFile member has actually been closed
+                //by other code.
+                //So, do one retry to drop a connection and get a fresh JarFile
+                LOG.warn("Retrying list:"+e);
+                LOG.debug(e);
+                release();
+                list = listEntries();
+            }
+
+            if (list != null)
+            {
+                _list=new String[list.size()];
+                list.toArray(_list);
+            }  
+        }
+        return _list;
+    }
+    
+    
+    /* ------------------------------------------------------------ */
+    private List<String> listEntries ()
+    {
+        checkConnection();
+        
+        ArrayList<String> list = new ArrayList<String>(32);
+        JarFile jarFile=_jarFile;
+        if(jarFile==null)
+        {
+            try
+            {
+                JarURLConnection jc=(JarURLConnection)((new URL(_jarUrl)).openConnection());
+                jc.setUseCaches(getUseCaches());
+                jarFile=jc.getJarFile();
+            }
+            catch(Exception e)
+            {
+
+                e.printStackTrace();
+                 LOG.ignore(e);
+            }
+        }
+        
+        Enumeration<JarEntry> e=jarFile.entries();
+        String dir=_urlString.substring(_urlString.indexOf("!/")+2);
+        while(e.hasMoreElements())
+        {
+            JarEntry entry = e.nextElement();               
+            String name=entry.getName().replace('\\','/');               
+            if(!name.startsWith(dir) || name.length()==dir.length())
+            {
+                continue;
+            }
+            String listName=name.substring(dir.length());               
+            int dash=listName.indexOf('/');
+            if (dash>=0)
+            {
+                //when listing jar:file urls, you get back one
+                //entry for the dir itself, which we ignore
+                if (dash==0 && listName.length()==1)
+                    continue;
+                //when listing jar:file urls, all files and
+                //subdirs have a leading /, which we remove
+                if (dash==0)
+                    listName=listName.substring(dash+1, listName.length());
+                else
+                    listName=listName.substring(0,dash+1);
+                
+                if (list.contains(listName))
+                    continue;
+            }
+            
+            list.add(listName);
+        }
+        
+        return list;
+    }
+    
+    
+    
+    
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Return the length of the resource
+     */
+    @Override
+    public long length()
+    {
+        if (isDirectory())
+            return -1;
+
+        if (_entry!=null)
+            return _entry.getSize();
+        
+        return -1;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Encode according to this resource type.
+     * File URIs are not encoded.
+     * @param uri URI to encode.
+     * @return The uri unchanged.
+     */
+    @Override
+    public String encode(String uri)
+    {
+        return uri;
+    }
+
+    
+    /**
+     * Take a Resource that possibly might use URLConnection caching
+     * and turn it into one that doesn't.
+     * @param resource
+     * @return the non-caching resource
+     */
+    public static Resource getNonCachingResource (Resource resource)
+    {
+        if (!(resource instanceof JarFileResource))
+            return resource;
+        
+        JarFileResource oldResource = (JarFileResource)resource;
+        
+        JarFileResource newResource = new JarFileResource(oldResource.getURL(), false);
+        return newResource;
+        
+    }
+    
+    /**
+     * Check if this jar:file: resource is contained in the
+     * named resource. Eg <code>jar:file:///a/b/c/foo.jar!/x.html</code> isContainedIn <code>file:///a/b/c/foo.jar</code>
+     * @param resource
+     * @return true if resource is contained in the named resource
+     * @throws MalformedURLException
+     */
+    @Override
+    public boolean isContainedIn (Resource resource) 
+    throws MalformedURLException
+    {
+        String string = _urlString;
+        int index = string.indexOf("!/");
+        if (index > 0)
+            string = string.substring(0,index);
+        if (string.startsWith("jar:"))
+            string = string.substring(4);
+        URL url = new URL(string);
+        return url.sameFile(resource.getURL());     
+    }
+}
+
+
+
+
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/resource/JarResource.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,273 @@
+//
+//  ========================================================================
+//  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.util.resource;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.JarURLConnection;
+import java.net.URL;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+import java.util.jar.Manifest;
+
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.URIUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+
+/* ------------------------------------------------------------ */
+public class JarResource extends URLResource
+{
+    private static final Logger LOG = Log.getLogger(JarResource.class);
+    protected JarURLConnection _jarConnection;
+    
+    /* -------------------------------------------------------- */
+    JarResource(URL url)
+    {
+        super(url,null);
+    }
+
+    /* ------------------------------------------------------------ */
+    JarResource(URL url, boolean useCaches)
+    {
+        super(url, null, useCaches);
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public synchronized void release()
+    {
+        _jarConnection=null;
+        super.release();
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    protected synchronized boolean checkConnection()
+    {
+        super.checkConnection();
+        try
+        {
+            if (_jarConnection!=_connection)
+                newConnection();
+        }
+        catch(IOException e)
+        {
+            LOG.ignore(e);
+            _jarConnection=null;
+        }
+        
+        return _jarConnection!=null;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @throws IOException Sub-classes of <code>JarResource</code> may throw an IOException (or subclass) 
+     */
+    protected void newConnection() throws IOException
+    {
+        _jarConnection=(JarURLConnection)_connection;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns true if the respresenetd resource exists.
+     */
+    @Override
+    public boolean exists()
+    {
+        if (_urlString.endsWith("!/"))
+            return checkConnection();
+        else
+            return super.exists();
+    }    
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public File getFile()
+        throws IOException
+    {
+        return null;
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public InputStream getInputStream()
+        throws java.io.IOException
+    {     
+        checkConnection();
+        if (!_urlString.endsWith("!/"))
+            return new FilterInputStream(super.getInputStream()) 
+            {
+                @Override
+                public void close() throws IOException {this.in=IO.getClosedStream();}
+            };
+
+        URL url = new URL(_urlString.substring(4,_urlString.length()-2));      
+        InputStream is = url.openStream();
+        return is;
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public void copyTo(File directory)
+        throws IOException
+    {
+        if (!exists())
+            return;
+        
+        if(LOG.isDebugEnabled())
+            LOG.debug("Extract "+this+" to "+directory);
+        
+        String urlString = this.getURL().toExternalForm().trim();
+        int endOfJarUrl = urlString.indexOf("!/");
+        int startOfJarUrl = (endOfJarUrl >= 0?4:0);
+        
+        if (endOfJarUrl < 0)
+            throw new IOException("Not a valid jar url: "+urlString);
+        
+        URL jarFileURL = new URL(urlString.substring(startOfJarUrl, endOfJarUrl));
+        String subEntryName = (endOfJarUrl+2 < urlString.length() ? urlString.substring(endOfJarUrl + 2) : null);
+        boolean subEntryIsDir = (subEntryName != null && subEntryName.endsWith("/")?true:false);
+      
+        if (LOG.isDebugEnabled()) 
+            LOG.debug("Extracting entry = "+subEntryName+" from jar "+jarFileURL);
+        
+        InputStream is = jarFileURL.openConnection().getInputStream();
+        JarInputStream jin = new JarInputStream(is);
+        JarEntry entry;
+        boolean shouldExtract;
+        while((entry=jin.getNextJarEntry())!=null)
+        {
+            String entryName = entry.getName();
+            if ((subEntryName != null) && (entryName.startsWith(subEntryName)))
+            { 
+                // is the subentry really a dir?
+                if (!subEntryIsDir && subEntryName.length()+1==entryName.length() && entryName.endsWith("/"))
+                        subEntryIsDir=true;
+                
+                //if there is a particular subEntry that we are looking for, only
+                //extract it.
+                if (subEntryIsDir)
+                {
+                    //if it is a subdirectory we are looking for, then we
+                    //are looking to extract its contents into the target
+                    //directory. Remove the name of the subdirectory so
+                    //that we don't wind up creating it too.
+                    entryName = entryName.substring(subEntryName.length());
+                    if (!entryName.equals(""))
+                    {
+                        //the entry is 
+                        shouldExtract = true;                   
+                    }
+                    else
+                        shouldExtract = false;
+                }
+                else
+                    shouldExtract = true;
+            }
+            else if ((subEntryName != null) && (!entryName.startsWith(subEntryName)))
+            {
+                //there is a particular entry we are looking for, and this one
+                //isn't it
+                shouldExtract = false;
+            }
+            else
+            {
+                //we are extracting everything
+                shouldExtract =  true;
+            }
+                
+            
+            if (!shouldExtract)
+            {
+                if (LOG.isDebugEnabled()) 
+                    LOG.debug("Skipping entry: "+entryName);
+                continue;
+            }
+                
+            String dotCheck = entryName.replace('\\', '/');   
+            dotCheck = URIUtil.canonicalPath(dotCheck);
+            if (dotCheck == null)
+            {
+                if (LOG.isDebugEnabled()) 
+                    LOG.debug("Invalid entry: "+entryName);
+                continue;
+            }
+
+            File file=new File(directory,entryName);
+     
+            if (entry.isDirectory())
+            {
+                // Make directory
+                if (!file.exists())
+                    file.mkdirs();
+            }
+            else
+            {
+                // make directory (some jars don't list dirs)
+                File dir = new File(file.getParent());
+                if (!dir.exists())
+                    dir.mkdirs();
+
+                // Make file
+                FileOutputStream fout = null;
+                try
+                {
+                    fout = new FileOutputStream(file);
+                    IO.copy(jin,fout);
+                }
+                finally
+                {
+                    IO.close(fout);
+                }
+
+                // touch the file.
+                if (entry.getTime()>=0)
+                    file.setLastModified(entry.getTime());
+            }
+        }
+        
+        if ((subEntryName == null) || (subEntryName != null && subEntryName.equalsIgnoreCase("META-INF/MANIFEST.MF")))
+        {
+            Manifest manifest = jin.getManifest();
+            if (manifest != null)
+            {
+                File metaInf = new File (directory, "META-INF");
+                metaInf.mkdir();
+                File f = new File(metaInf, "MANIFEST.MF");
+                FileOutputStream fout = new FileOutputStream(f);
+                manifest.write(fout);
+                fout.close();   
+            }
+        }
+        IO.close(jin);
+    }   
+    
+    public static Resource newJarResource(Resource resource) throws IOException
+    {
+        if (resource instanceof JarResource)
+            return resource;
+        return Resource.newResource("jar:" + resource + "!/");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/resource/Resource.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,678 @@
+//
+//  ========================================================================
+//  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.util.resource;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLConnection;
+import java.text.DateFormat;
+import java.util.Arrays;
+import java.util.Date;
+
+import org.eclipse.jetty.util.B64Code;
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.Loader;
+import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.URIUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+
+/* ------------------------------------------------------------ */
+/** 
+ * Abstract resource class.
+ */
+public abstract class Resource implements ResourceFactory
+{
+    private static final Logger LOG = Log.getLogger(Resource.class);
+    public static boolean __defaultUseCaches = true;
+    volatile Object _associate;
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Change the default setting for url connection caches.
+     * Subsequent URLConnections will use this default.
+     * @param useCaches
+     */
+    public static void setDefaultUseCaches (boolean useCaches)
+    {
+        __defaultUseCaches=useCaches;
+    }
+
+    /* ------------------------------------------------------------ */
+    public static boolean getDefaultUseCaches ()
+    {
+        return __defaultUseCaches;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Construct a resource from a uri.
+     * @param uri A URI.
+     * @return A Resource object.
+     * @throws IOException Problem accessing URI
+     */
+    public static Resource newResource(URI uri)
+        throws IOException
+    {
+        return newResource(uri.toURL());
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Construct a resource from a url.
+     * @param url A URL.
+     * @return A Resource object.
+     * @throws IOException Problem accessing URL
+     */
+    public static Resource newResource(URL url)
+        throws IOException
+    {
+        return newResource(url, __defaultUseCaches);
+    }
+    
+    /* ------------------------------------------------------------ */   
+    /**
+     * Construct a resource from a url.
+     * @param url the url for which to make the resource
+     * @param useCaches true enables URLConnection caching if applicable to the type of resource
+     * @return
+     */
+    static Resource newResource(URL url, boolean useCaches)
+    {
+        if (url==null)
+            return null;
+
+        String url_string=url.toExternalForm();
+        if( url_string.startsWith( "file:"))
+        {
+            try
+            {
+                FileResource fileResource= new FileResource(url);
+                return fileResource;
+            }
+            catch(Exception e)
+            {
+                LOG.debug(Log.EXCEPTION,e);
+                return new BadResource(url,e.toString());
+            }
+        }
+        else if( url_string.startsWith( "jar:file:"))
+        {
+            return new JarFileResource(url, useCaches);
+        }
+        else if( url_string.startsWith( "jar:"))
+        {
+            return new JarResource(url, useCaches);
+        }
+
+        return new URLResource(url,null,useCaches);
+    }
+
+    
+    
+    /* ------------------------------------------------------------ */
+    /** Construct a resource from a string.
+     * @param resource A URL or filename.
+     * @return A Resource object.
+     */
+    public static Resource newResource(String resource)
+        throws MalformedURLException, IOException
+    {
+        return newResource(resource, __defaultUseCaches);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Construct a resource from a string.
+     * @param resource A URL or filename.
+     * @param useCaches controls URLConnection caching
+     * @return A Resource object.
+     */
+    public static Resource newResource (String resource, boolean useCaches)       
+    throws MalformedURLException, IOException
+    {
+        URL url=null;
+        try
+        {
+            // Try to format as a URL?
+            url = new URL(resource);
+        }
+        catch(MalformedURLException e)
+        {
+            if(!resource.startsWith("ftp:") &&
+               !resource.startsWith("file:") &&
+               !resource.startsWith("jar:"))
+            {
+                try
+                {
+                    // It's a file.
+                    if (resource.startsWith("./"))
+                        resource=resource.substring(2);
+                    
+                    File file=new File(resource).getCanonicalFile();
+                    url=Resource.toURL(file);            
+                    
+                    URLConnection connection=url.openConnection();
+                    connection.setUseCaches(useCaches);
+                    return new FileResource(url,connection,file);
+                }
+                catch(Exception e2)
+                {
+                    LOG.debug(Log.EXCEPTION,e2);
+                    throw e;
+                }
+            }
+            else
+            {
+                LOG.warn("Bad Resource: "+resource);
+                throw e;
+            }
+        }
+
+        return newResource(url);
+    }
+
+    /* ------------------------------------------------------------ */
+    public static Resource newResource (File file)
+    throws MalformedURLException, IOException
+    {
+        file = file.getCanonicalFile();
+        URL url = Resource.toURL(file);
+
+        URLConnection connection = url.openConnection();
+        FileResource fileResource = new FileResource(url, connection, file);
+        return fileResource;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Construct a system resource from a string.
+     * The resource is tried as classloader resource before being
+     * treated as a normal resource.
+     * @param resource Resource as string representation 
+     * @return The new Resource
+     * @throws IOException Problem accessing resource.
+     */
+    public static Resource newSystemResource(String resource)
+        throws IOException
+    {
+        URL url=null;
+        // Try to format as a URL?
+        ClassLoader loader=Thread.currentThread().getContextClassLoader();
+        if (loader!=null)
+        {
+            try
+            {
+                url = loader.getResource(resource);
+                if (url == null && resource.startsWith("/"))
+                    url = loader.getResource(resource.substring(1));
+            }
+            catch (IllegalArgumentException e)
+            {
+                // Catches scenario where a bad Windows path like "C:\dev" is
+                // improperly escaped, which various downstream classloaders
+                // tend to have a problem with
+                url = null;
+            }
+        }
+        if (url==null)
+        {
+            loader=Resource.class.getClassLoader();
+            if (loader!=null)
+            {
+                url=loader.getResource(resource);
+                if (url==null && resource.startsWith("/"))
+                    url=loader.getResource(resource.substring(1));
+            }
+        }
+        
+        if (url==null)
+        {
+            url=ClassLoader.getSystemResource(resource);
+            if (url==null && resource.startsWith("/"))
+                url=ClassLoader.getSystemResource(resource.substring(1));
+        }
+        
+        if (url==null)
+            return null;
+        
+        return newResource(url);
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Find a classpath resource.
+     */
+    public static Resource newClassPathResource(String resource)
+    {
+        return newClassPathResource(resource,true,false);
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Find a classpath resource.
+     * The {@link java.lang.Class#getResource(String)} method is used to lookup the resource. If it is not
+     * found, then the {@link Loader#getResource(Class, String, boolean)} method is used.
+     * If it is still not found, then {@link ClassLoader#getSystemResource(String)} is used.
+     * Unlike {@link ClassLoader#getSystemResource(String)} this method does not check for normal resources.
+     * @param name The relative name of the resource
+     * @param useCaches True if URL caches are to be used.
+     * @param checkParents True if forced searching of parent Classloaders is performed to work around 
+     * loaders with inverted priorities
+     * @return Resource or null
+     */
+    public static Resource newClassPathResource(String name,boolean useCaches,boolean checkParents)
+    {
+        URL url=Resource.class.getResource(name);
+        
+        if (url==null)
+            url=Loader.getResource(Resource.class,name,checkParents);
+        if (url==null)
+            return null;
+        return newResource(url,useCaches);
+    }
+    
+    /* ------------------------------------------------------------ */
+    public static boolean isContainedIn (Resource r, Resource containingResource) throws MalformedURLException
+    {
+        return r.isContainedIn(containingResource);
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    protected void finalize()
+    {
+        release();
+    }
+    
+    /* ------------------------------------------------------------ */
+    public abstract boolean isContainedIn (Resource r) throws MalformedURLException;
+    
+    
+    /* ------------------------------------------------------------ */
+    /** Release any temporary resources held by the resource.
+     */
+    public abstract void release();
+    
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns true if the respresened resource exists.
+     */
+    public abstract boolean exists();
+    
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns true if the respresenetd resource is a container/directory.
+     * If the resource is not a file, resources ending with "/" are
+     * considered directories.
+     */
+    public abstract boolean isDirectory();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns the last modified time
+     */
+    public abstract long lastModified();
+
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Return the length of the resource
+     */
+    public abstract long length();
+    
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns an URL representing the given resource
+     */
+    public abstract URL getURL();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns an URI representing the given resource
+     */
+    public URI getURI()
+    {
+        try
+        {
+            return getURL().toURI();
+        }
+        catch(Exception e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+    
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns an File representing the given resource or NULL if this
+     * is not possible.
+     */
+    public abstract File getFile()
+        throws IOException;
+    
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns the name of the resource
+     */
+    public abstract String getName();
+    
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns an input stream to the resource
+     */
+    public abstract InputStream getInputStream()
+        throws java.io.IOException;
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns an output stream to the resource
+     */
+    public abstract OutputStream getOutputStream()
+        throws java.io.IOException, SecurityException;
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Deletes the given resource
+     */
+    public abstract boolean delete()
+        throws SecurityException;
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Rename the given resource
+     */
+    public abstract boolean renameTo( Resource dest)
+        throws SecurityException;
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns a list of resource names contained in the given resource
+     * The resource names are not URL encoded.
+     */
+    public abstract String[] list();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns the resource contained inside the current resource with the
+     * given name.
+     * @param path The path segment to add, which should be encoded by the
+     * encode method. 
+     */
+    public abstract Resource addPath(String path)
+        throws IOException,MalformedURLException;
+
+    /* ------------------------------------------------------------ */
+    /** Get a resource from withing this resource.
+     * <p>
+     * This method is essentially an alias for {@link #addPath(String)}, but without checked exceptions.
+     * This method satisfied the {@link ResourceFactory} interface.
+     * @see org.eclipse.jetty.util.resource.ResourceFactory#getResource(java.lang.String)
+     */
+    public Resource getResource(String path)
+    {
+        try
+        {
+            return addPath(path);
+        }
+        catch(Exception e)
+        {
+            LOG.debug(e);
+            return null;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Encode according to this resource type.
+     * The default implementation calls URI.encodePath(uri)
+     * @param uri 
+     * @return String encoded for this resource type.
+     */
+    public String encode(String uri)
+    {
+        return URIUtil.encodePath(uri);
+    }
+        
+    /* ------------------------------------------------------------ */
+    public Object getAssociate()
+    {
+        return _associate;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setAssociate(Object o)
+    {
+        _associate=o;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The canonical Alias of this resource or null if none.
+     */
+    public URL getAlias()
+    {
+        return null;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Get the resource list as a HTML directory listing.
+     * @param base The base URL
+     * @param parent True if the parent directory should be included
+     * @return String of HTML
+     */
+    public String getListHTML(String base,boolean parent)
+        throws IOException
+    {
+        base=URIUtil.canonicalPath(base);
+        if (base==null || !isDirectory())
+            return null;
+        
+        String[] ls = list();
+        if (ls==null)
+            return null;
+        Arrays.sort(ls);
+        
+        String decodedBase = URIUtil.decodePath(base);
+        String title = "Directory: "+deTag(decodedBase);
+
+        StringBuilder buf=new StringBuilder(4096);
+        buf.append("<HTML><HEAD>");
+        buf.append("<LINK HREF=\"").append("jetty-dir.css").append("\" REL=\"stylesheet\" TYPE=\"text/css\"/><TITLE>");
+        buf.append(title);
+        buf.append("</TITLE></HEAD><BODY>\n<H1>");
+        buf.append(title);
+        buf.append("</H1>\n<TABLE BORDER=0>\n");
+        
+        if (parent)
+        {
+            buf.append("<TR><TD><A HREF=\"");
+            buf.append(URIUtil.addPaths(base,"../"));
+            buf.append("\">Parent Directory</A></TD><TD></TD><TD></TD></TR>\n");
+        }
+        
+        String encodedBase = hrefEncodeURI(base);
+        
+        DateFormat dfmt=DateFormat.getDateTimeInstance(DateFormat.MEDIUM,
+                                                       DateFormat.MEDIUM);
+        for (int i=0 ; i< ls.length ; i++)
+        {
+            Resource item = addPath(ls[i]);
+            
+            buf.append("\n<TR><TD><A HREF=\"");
+            String path=URIUtil.addPaths(encodedBase,URIUtil.encodePath(ls[i]));
+            
+            buf.append(path);
+            
+            if (item.isDirectory() && !path.endsWith("/"))
+                buf.append(URIUtil.SLASH);
+            
+            // URIUtil.encodePath(buf,path);
+            buf.append("\">");
+            buf.append(deTag(ls[i]));
+            buf.append("&nbsp;");
+            buf.append("</A></TD><TD ALIGN=right>");
+            buf.append(item.length());
+            buf.append(" bytes&nbsp;</TD><TD>");
+            buf.append(dfmt.format(new Date(item.lastModified())));
+            buf.append("</TD></TR>");
+        }
+        buf.append("</TABLE>\n");
+	buf.append("</BODY></HTML>\n");
+        
+        return buf.toString();
+    }
+    
+    /**
+     * Encode any characters that could break the URI string in an HREF.
+     * Such as <a href="/path/to;<script>Window.alert("XSS"+'%20'+"here");</script>">Link</a>
+     * 
+     * The above example would parse incorrectly on various browsers as the "<" or '"' characters
+     * would end the href attribute value string prematurely.
+     * 
+     * @param raw the raw text to encode.
+     * @return the defanged text.
+     */
+    private static String hrefEncodeURI(String raw) 
+    {
+        StringBuffer buf = null;
+
+        loop:
+        for (int i=0;i<raw.length();i++)
+        {
+            char c=raw.charAt(i);
+            switch(c)
+            {
+                case '\'':
+                case '"':
+                case '<':
+                case '>':
+                    buf=new StringBuffer(raw.length()<<1);
+                    break loop;
+            }
+        }
+        if (buf==null)
+            return raw;
+
+        for (int i=0;i<raw.length();i++)
+        {
+            char c=raw.charAt(i);       
+            switch(c)
+            {
+              case '"':
+                  buf.append("%22");
+                  continue;
+              case '\'':
+                  buf.append("%27");
+                  continue;
+              case '<':
+                  buf.append("%3C");
+                  continue;
+              case '>':
+                  buf.append("%3E");
+                  continue;
+              default:
+                  buf.append(c);
+                  continue;
+            }
+        }
+
+        return buf.toString();
+    }
+    
+    private static String deTag(String raw) 
+    {
+        return StringUtil.replace( StringUtil.replace(raw,"<","&lt;"), ">", "&gt;");
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** 
+     * @param out 
+     * @param start First byte to write
+     * @param count Bytes to write or -1 for all of them.
+     */
+    public void writeTo(OutputStream out,long start,long count)
+        throws IOException
+    {
+        InputStream in = getInputStream();
+        try
+        {
+            in.skip(start);
+            if (count<0)
+                IO.copy(in,out);
+            else
+                IO.copy(in,out,count);
+        }
+        finally
+        {
+            in.close();
+        }
+    }    
+    
+    /* ------------------------------------------------------------ */
+    public void copyTo(File destination)
+        throws IOException
+    {
+        if (destination.exists())
+            throw new IllegalArgumentException(destination+" exists");
+        writeTo(new FileOutputStream(destination),0,-1);
+    }
+
+    /* ------------------------------------------------------------ */
+    public String getWeakETag()
+    {
+        try
+        {
+            StringBuilder b = new StringBuilder(32);
+            b.append("W/\"");
+            
+            String name=getName();
+            int length=name.length();
+            long lhash=0;
+            for (int i=0; i<length;i++)
+                lhash=31*lhash+name.charAt(i);
+            
+            B64Code.encode(lastModified()^lhash,b);
+            B64Code.encode(length()^lhash,b);
+            b.append('"');
+            return b.toString();
+        } 
+        catch (IOException e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** Generate a properly encoded URL from a {@link File} instance.
+     * @param file Target file. 
+     * @return URL of the target file.
+     * @throws MalformedURLException 
+     */
+    public static URL toURL(File file) throws MalformedURLException
+    {
+        return file.toURI().toURL();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/resource/ResourceCollection.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,482 @@
+//
+//  ========================================================================
+//  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.util.resource;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.eclipse.jetty.util.URIUtil;
+
+/**
+ * A collection of resources (dirs).
+ * Allows webapps to have multiple (static) sources.
+ * The first resource in the collection is the main resource.
+ * If a resource is not found in the main resource, it looks it up in 
+ * the order the resources were constructed.
+ * 
+ * 
+ *
+ */
+public class ResourceCollection extends Resource
+{
+    private Resource[] _resources;
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Instantiates an empty resource collection.
+     * 
+     * This constructor is used when configuring jetty-maven-plugin.
+     */
+    public ResourceCollection()
+    {
+        _resources = new Resource[0];
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Instantiates a new resource collection.
+     *
+     * @param resources the resources to be added to collection
+     */
+    public ResourceCollection(Resource... resources)
+    {
+        List<Resource> list = new ArrayList<Resource>();
+        for (Resource r : resources)
+        {
+            if (r==null)
+                continue;
+            if (r instanceof ResourceCollection)
+            {
+                for (Resource r2 : ((ResourceCollection)r).getResources())
+                    list.add(r2);
+            }
+            else
+                list.add(r);
+        }
+        _resources = list.toArray(new Resource[list.size()]);
+        for(Resource r : _resources)
+        {
+            if(!r.exists() || !r.isDirectory())
+                throw new IllegalArgumentException(r + " is not an existing directory.");
+        }
+    }
+    
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Instantiates a new resource collection.
+     *
+     * @param resources the resource strings to be added to collection
+     */
+    public ResourceCollection(String[] resources)
+    {
+        _resources = new Resource[resources.length];
+        try
+        {
+            for(int i=0; i<resources.length; i++)
+            {
+                _resources[i] = Resource.newResource(resources[i]);
+                if(!_resources[i].exists() || !_resources[i].isDirectory())
+                    throw new IllegalArgumentException(_resources[i] + " is not an existing directory.");
+            }
+        }
+        catch(IllegalArgumentException e)
+        {
+            throw e;
+        }
+        catch(Exception e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Instantiates a new resource collection.
+     *
+     * @param csvResources the string containing comma-separated resource strings
+     */
+    public ResourceCollection(String csvResources)
+    {
+        setResourcesAsCSV(csvResources);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Retrieves the resource collection's resources.
+     * 
+     * @return the resource array
+     */
+    public Resource[] getResources()
+    {
+        return _resources;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Sets the resource collection's resources.
+     *
+     * @param resources the new resource array
+     */
+    public void setResources(Resource[] resources)
+    {
+        _resources = resources != null ? resources : new Resource[0];
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Sets the resources as string of comma-separated values.
+     * This method should be used when configuring jetty-maven-plugin.
+     *
+     * @param csvResources the comma-separated string containing
+     *                     one or more resource strings.
+     */
+    public void setResourcesAsCSV(String csvResources)
+    {
+        StringTokenizer tokenizer = new StringTokenizer(csvResources, ",;");
+        int len = tokenizer.countTokens();
+        if(len==0)
+        {
+            throw new IllegalArgumentException("ResourceCollection@setResourcesAsCSV(String) " +
+                    " argument must be a string containing one or more comma-separated resource strings.");
+        }
+        
+        _resources = new Resource[len];
+        try
+        {            
+            for(int i=0; tokenizer.hasMoreTokens(); i++)
+            {
+                _resources[i] = Resource.newResource(tokenizer.nextToken().trim());
+                if(!_resources[i].exists() || !_resources[i].isDirectory())
+                    throw new IllegalArgumentException(_resources[i] + " is not an existing directory.");
+            }
+        }
+        catch(Exception e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @param path The path segment to add
+     * @return The contained resource (found first) in the collection of resources
+     */
+    @Override
+    public Resource addPath(String path) throws IOException, MalformedURLException
+    {
+        if(_resources==null)
+            throw new IllegalStateException("*resources* not set.");
+        
+        if(path==null)
+            throw new MalformedURLException();
+        
+        if(path.length()==0 || URIUtil.SLASH.equals(path))
+            return this;
+        
+        Resource resource=null;
+        ArrayList<Resource> resources = null;
+        int i=0;
+        for(; i<_resources.length; i++)
+        {
+            resource = _resources[i].addPath(path);  
+            if (resource.exists())
+            {
+                if (resource.isDirectory())
+                    break;       
+                return resource;
+            }
+        }  
+
+        for(i++; i<_resources.length; i++)
+        {
+            Resource r = _resources[i].addPath(path); 
+            if (r.exists() && r.isDirectory())
+            {
+                if (resource!=null)
+                {
+                    resources = new ArrayList<Resource>();
+                    resources.add(resource);
+                    resource=null;
+                }
+                resources.add(r);
+            }
+        }
+
+        if (resource!=null)
+            return resource;
+        if (resources!=null)
+            return new ResourceCollection(resources.toArray(new Resource[resources.size()]));
+        return null;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @param path
+     * @return the resource(file) if found, returns a list of resource dirs if its a dir, else null.
+     * @throws IOException
+     * @throws MalformedURLException
+     */
+    protected Object findResource(String path) throws IOException, MalformedURLException
+    {        
+        Resource resource=null;
+        ArrayList<Resource> resources = null;
+        int i=0;
+        for(; i<_resources.length; i++)
+        {
+            resource = _resources[i].addPath(path);  
+            if (resource.exists())
+            {
+                if (resource.isDirectory())
+                    break;
+               
+                return resource;
+            }
+        }  
+
+        for(i++; i<_resources.length; i++)
+        {
+            Resource r = _resources[i].addPath(path); 
+            if (r.exists() && r.isDirectory())
+            {
+                if (resource!=null)
+                {
+                    resources = new ArrayList<Resource>();
+                    resources.add(resource);
+                }
+                resources.add(r);
+            }
+        }
+        
+        if (resource!=null)
+            return resource;
+        if (resources!=null)
+            return resources;
+        return null;
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public boolean delete() throws SecurityException
+    {
+        throw new UnsupportedOperationException();
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public boolean exists()
+    {
+        if(_resources==null)
+            throw new IllegalStateException("*resources* not set.");
+        
+        return true;
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public File getFile() throws IOException
+    {
+        if(_resources==null)
+            throw new IllegalStateException("*resources* not set.");
+        
+        for(Resource r : _resources)
+        {
+            File f = r.getFile();
+            if(f!=null)
+                return f;
+        }
+        return null;
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public InputStream getInputStream() throws IOException
+    {
+        if(_resources==null)
+            throw new IllegalStateException("*resources* not set.");
+        
+        for(Resource r : _resources)
+        {
+            InputStream is = r.getInputStream();
+            if(is!=null)
+                return is;
+        }
+        return null;
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public String getName()
+    {
+        if(_resources==null)
+            throw new IllegalStateException("*resources* not set.");
+        
+        for(Resource r : _resources)
+        {
+            String name = r.getName();
+            if(name!=null)
+                return name;
+        }
+        return null;
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public OutputStream getOutputStream() throws IOException, SecurityException
+    {
+        if(_resources==null)
+            throw new IllegalStateException("*resources* not set.");
+        
+        for(Resource r : _resources)
+        {
+            OutputStream os = r.getOutputStream();
+            if(os!=null)
+                return os;
+        }
+        return null;
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public URL getURL()
+    {
+        if(_resources==null)
+            throw new IllegalStateException("*resources* not set.");
+        
+        for(Resource r : _resources)
+        {
+            URL url = r.getURL();
+            if(url!=null)
+                return url;
+        }
+        return null;
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public boolean isDirectory()
+    {
+        if(_resources==null)
+            throw new IllegalStateException("*resources* not set.");
+        
+        return true;
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public long lastModified()
+    {
+        if(_resources==null)
+            throw new IllegalStateException("*resources* not set.");
+        
+        for(Resource r : _resources)
+        {
+            long lm = r.lastModified();
+            if (lm!=-1)
+                return lm;
+        }
+        return -1;
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public long length()
+    {
+        return -1;
+    }    
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The list of resource names(merged) contained in the collection of resources.
+     */    
+    @Override
+    public String[] list()
+    {
+        if(_resources==null)
+            throw new IllegalStateException("*resources* not set.");
+        
+        HashSet<String> set = new HashSet<String>();
+        for(Resource r : _resources)
+        {
+            for(String s : r.list())
+                set.add(s);
+        }
+        String[] result=set.toArray(new String[set.size()]);
+        Arrays.sort(result);
+        return result;
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public void release()
+    {
+        if(_resources==null)
+            throw new IllegalStateException("*resources* not set.");
+        
+        for(Resource r : _resources)
+            r.release();
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public boolean renameTo(Resource dest) throws SecurityException
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void copyTo(File destination)
+        throws IOException
+    {
+        for (int r=_resources.length;r-->0;)
+            _resources[r].copyTo(destination);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @return the list of resources separated by a path separator
+     */
+    @Override
+    public String toString()
+    {
+        if(_resources==null)
+            return "[]";
+        
+        return String.valueOf(Arrays.asList(_resources));
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public boolean isContainedIn(Resource r) throws MalformedURLException
+    {
+        // TODO could look at implementing the semantic of is this collection a subset of the Resource r?
+        return false;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/resource/ResourceFactory.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,34 @@
+//
+//  ========================================================================
+//  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.util.resource;
+
+
+/* ------------------------------------------------------------ */
+/** ResourceFactory.
+ */
+public interface ResourceFactory
+{
+    
+    /* ------------------------------------------------------------ */
+    /** Get a resource for a path.
+     * @param path The path to the resource
+     * @return The resource or null 
+     */
+    Resource getResource(String path);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/resource/URLResource.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,321 @@
+//
+//  ========================================================================
+//  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.util.resource;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.Permission;
+
+import org.eclipse.jetty.util.URIUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/* ------------------------------------------------------------ */
+/** Abstract resource class.
+ */
+public class URLResource extends Resource
+{
+    private static final Logger LOG = Log.getLogger(URLResource.class);
+    protected URL _url;
+    protected String _urlString;
+    
+    protected URLConnection _connection;
+    protected InputStream _in=null;
+    transient boolean _useCaches = Resource.__defaultUseCaches;
+    
+    /* ------------------------------------------------------------ */
+    protected URLResource(URL url, URLConnection connection)
+    {
+        _url = url;
+        _urlString=_url.toString();
+        _connection=connection;
+    }
+    
+    /* ------------------------------------------------------------ */
+    protected URLResource (URL url, URLConnection connection, boolean useCaches)
+    {
+        this (url, connection);
+        _useCaches = useCaches;
+    }
+
+    /* ------------------------------------------------------------ */
+    protected synchronized boolean checkConnection()
+    {
+        if (_connection==null)
+        {
+            try{
+                _connection=_url.openConnection();
+                _connection.setUseCaches(_useCaches);
+            }
+            catch(IOException e)
+            {
+                LOG.ignore(e);
+            }
+        }
+        return _connection!=null;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Release any resources held by the resource.
+     */
+    @Override
+    public synchronized void release()
+    {
+        if (_in!=null)
+        {
+            try{_in.close();}catch(IOException e){LOG.ignore(e);}
+            _in=null;
+        }
+
+        if (_connection!=null)
+            _connection=null;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns true if the represented resource exists.
+     */
+    @Override
+    public boolean exists()
+    {
+        try
+        {
+            synchronized(this)
+            {
+                if (checkConnection() && _in==null )
+                    _in = _connection.getInputStream();
+            }
+        }
+        catch (IOException e)
+        {
+            LOG.ignore(e);
+        }
+        return _in!=null;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns true if the respresenetd resource is a container/directory.
+     * If the resource is not a file, resources ending with "/" are
+     * considered directories.
+     */
+    @Override
+    public boolean isDirectory()
+    {
+        return exists() && _url.toString().endsWith("/");
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns the last modified time
+     */
+    @Override
+    public long lastModified()
+    {
+        if (checkConnection())
+            return _connection.getLastModified();
+        return -1;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Return the length of the resource
+     */
+    @Override
+    public long length()
+    {
+        if (checkConnection())
+            return _connection.getContentLength();
+        return -1;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns an URL representing the given resource
+     */
+    @Override
+    public URL getURL()
+    {
+        return _url;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns an File representing the given resource or NULL if this
+     * is not possible.
+     */
+    @Override
+    public File getFile()
+        throws IOException
+    {
+        // Try the permission hack
+        if (checkConnection())
+        {
+            Permission perm = _connection.getPermission();
+            if (perm instanceof java.io.FilePermission)
+                return new File(perm.getName());
+        }
+
+        // Try the URL file arg
+        try {return new File(_url.getFile());}
+        catch(Exception e) {LOG.ignore(e);}
+
+        // Don't know the file
+        return null;    
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns the name of the resource
+     */
+    @Override
+    public String getName()
+    {
+        return _url.toExternalForm();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns an input stream to the resource
+     */
+    @Override
+    public synchronized InputStream getInputStream()
+        throws java.io.IOException
+    {
+        if (!checkConnection())
+            throw new IOException( "Invalid resource");
+
+        try
+        {    
+            if( _in != null)
+            {
+                InputStream in = _in;
+                _in=null;
+                return in;
+            }
+            return _connection.getInputStream();
+        }
+        finally
+        {
+            _connection=null;
+        }
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns an output stream to the resource
+     */
+    @Override
+    public OutputStream getOutputStream()
+        throws java.io.IOException, SecurityException
+    {
+        throw new IOException( "Output not supported");
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Deletes the given resource
+     */
+    @Override
+    public boolean delete()
+        throws SecurityException
+    {
+        throw new SecurityException( "Delete not supported");
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Rename the given resource
+     */
+    @Override
+    public boolean renameTo( Resource dest)
+        throws SecurityException
+    {
+        throw new SecurityException( "RenameTo not supported");
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns a list of resource names contained in the given resource
+     */
+    @Override
+    public String[] list()
+    {
+        return null;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns the resource contained inside the current resource with the
+     * given name
+     */
+    @Override
+    public Resource addPath(String path)
+        throws IOException,MalformedURLException
+    {
+        if (path==null)
+            return null;
+
+        path = URIUtil.canonicalPath(path);
+
+        return newResource(URIUtil.addPaths(_url.toExternalForm(),path));
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public String toString()
+    {
+        return _urlString;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public int hashCode()
+    {
+        return _urlString.hashCode();
+    }
+    
+    /* ------------------------------------------------------------ */
+    @Override
+    public boolean equals( Object o)
+    {
+        return o instanceof URLResource && _urlString.equals(((URLResource)o)._urlString);
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean getUseCaches ()
+    {
+        return _useCaches;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public boolean isContainedIn (Resource containingResource) throws MalformedURLException
+    {
+        return false; //TODO check this!
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/security/B64Code.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,33 @@
+//
+//  ========================================================================
+//  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.util.security;
+
+
+/* ------------------------------------------------------------ */
+/**
+ * @deprecated use {@link org.eclipse.jetty.util.B64Code}
+ */
+@Deprecated 
+public class B64Code extends org.eclipse.jetty.util.B64Code
+{
+    public B64Code()
+    {
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/security/CertificateUtils.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,94 @@
+//
+//  ========================================================================
+//  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.util.security;
+
+import java.io.InputStream;
+import java.security.KeyStore;
+import java.security.cert.CRL;
+import java.security.cert.CertificateFactory;
+import java.util.Collection;
+
+import org.eclipse.jetty.util.resource.Resource;
+
+public class CertificateUtils
+{
+    /* ------------------------------------------------------------ */
+    public static KeyStore getKeyStore(InputStream storeStream, String storePath, String storeType, String storeProvider, String storePassword) throws Exception
+    {
+        KeyStore keystore = null;
+
+        if (storeStream != null || storePath != null)
+        {
+            InputStream inStream = storeStream;
+            try
+            {
+                if (inStream == null)
+                {
+                    inStream = Resource.newResource(storePath).getInputStream();
+                }
+                
+                if (storeProvider != null)
+                {
+                    keystore = KeyStore.getInstance(storeType, storeProvider);
+                }
+                else
+                {
+                    keystore = KeyStore.getInstance(storeType);
+                }
+    
+                keystore.load(inStream, storePassword == null ? null : storePassword.toCharArray());
+            }
+            finally
+            {
+                if (inStream != null)
+                {
+                    inStream.close();
+                }
+            }
+        }
+        
+        return keystore;
+    }
+
+    /* ------------------------------------------------------------ */
+    public static Collection<? extends CRL> loadCRL(String crlPath) throws Exception
+    {
+        Collection<? extends CRL> crlList = null;
+
+        if (crlPath != null)
+        {
+            InputStream in = null;
+            try
+            {
+                in = Resource.newResource(crlPath).getInputStream();
+                crlList = CertificateFactory.getInstance("X.509").generateCRLs(in);
+            }
+            finally
+            {
+                if (in != null)
+                {
+                    in.close();
+                }
+            }
+        }
+
+        return crlList;
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/security/CertificateValidator.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,343 @@
+//
+//  ========================================================================
+//  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.util.security;
+
+import java.security.GeneralSecurityException;
+import java.security.InvalidParameterException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.Security;
+import java.security.cert.CRL;
+import java.security.cert.CertPathBuilder;
+import java.security.cert.CertPathBuilderResult;
+import java.security.cert.CertPathValidator;
+import java.security.cert.CertStore;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CollectionCertStoreParameters;
+import java.security.cert.PKIXBuilderParameters;
+import java.security.cert.X509CertSelector;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * Convenience class to handle validation of certificates, aliases and keystores
+ *
+ * Allows specifying Certificate Revocation List (CRL), as well as enabling
+ * CRL Distribution Points Protocol (CRLDP) certificate extension support,
+ * and also enabling On-Line Certificate Status Protocol (OCSP) support.
+ * 
+ * IMPORTANT: at least one of the above mechanisms *MUST* be configured and
+ * operational, otherwise certificate validation *WILL FAIL* unconditionally.
+ */
+public class CertificateValidator
+{
+    private static final Logger LOG = Log.getLogger(CertificateValidator.class);
+    private static AtomicLong __aliasCount = new AtomicLong();
+    
+    private KeyStore _trustStore;
+    private Collection<? extends CRL> _crls;
+
+    /** Maximum certification path length (n - number of intermediate certs, -1 for unlimited) */
+    private int _maxCertPathLength = -1;
+    /** CRL Distribution Points (CRLDP) support */
+    private boolean _enableCRLDP = false;
+    /** On-Line Certificate Status Protocol (OCSP) support */
+    private boolean _enableOCSP = false;
+    /** Location of OCSP Responder */
+    private String _ocspResponderURL;
+    
+    /**
+     * creates an instance of the certificate validator 
+     *
+     * @param trustStore 
+     * @param crls
+     */
+    public CertificateValidator(KeyStore trustStore, Collection<? extends CRL> crls)
+    {
+        if (trustStore == null)
+        {
+            throw new InvalidParameterException("TrustStore must be specified for CertificateValidator.");
+        }
+        
+        _trustStore = trustStore;
+        _crls = crls;
+    }
+    
+    /**
+     * validates all aliases inside of a given keystore
+     * 
+     * @param keyStore
+     * @throws CertificateException
+     */
+    public void validate( KeyStore keyStore ) throws CertificateException
+    {
+        try
+        {
+            Enumeration<String> aliases = keyStore.aliases();
+            
+            for ( ; aliases.hasMoreElements(); )
+            {
+                String alias = aliases.nextElement();
+                
+                validate(keyStore,alias);
+            }
+            
+        }
+        catch ( KeyStoreException kse )
+        {
+            throw new CertificateException("Unable to retrieve aliases from keystore", kse);
+        }
+    }
+    
+
+    /**
+     * validates a specific alias inside of the keystore being passed in
+     * 
+     * @param keyStore
+     * @param keyAlias
+     * @return the keyAlias if valid
+     * @throws CertificateException
+     */
+    public String validate(KeyStore keyStore, String keyAlias) throws CertificateException
+    {
+        String result = null;
+
+        if (keyAlias != null)
+        {
+            try
+            {
+                validate(keyStore, keyStore.getCertificate(keyAlias));
+            }
+            catch (KeyStoreException kse)
+            {
+                LOG.debug(kse);
+                throw new CertificateException("Unable to validate certificate" +
+                        " for alias [" + keyAlias + "]: " + kse.getMessage(), kse);
+            }
+            result = keyAlias;            
+        }
+        
+        return result;
+    }
+    
+    /**
+     * validates a specific certificate inside of the keystore being passed in
+     * 
+     * @param keyStore
+     * @param cert
+     * @throws CertificateException
+     */
+    public void validate(KeyStore keyStore, Certificate cert) throws CertificateException
+    {
+        Certificate[] certChain = null;
+        
+        if (cert != null && cert instanceof X509Certificate)
+        {
+            ((X509Certificate)cert).checkValidity();
+            
+            String certAlias = null;
+            try
+            {
+                if (keyStore == null)
+                {
+                    throw new InvalidParameterException("Keystore cannot be null");
+                }
+
+                certAlias = keyStore.getCertificateAlias((X509Certificate)cert);
+                if (certAlias == null)
+                {
+                    certAlias = "JETTY" + String.format("%016X",__aliasCount.incrementAndGet());
+                    keyStore.setCertificateEntry(certAlias, cert);
+                }
+                
+                certChain = keyStore.getCertificateChain(certAlias);
+                if (certChain == null || certChain.length == 0)
+                {
+                    throw new IllegalStateException("Unable to retrieve certificate chain");
+                }
+            }
+            catch (KeyStoreException kse)
+            {
+                LOG.debug(kse);
+                throw new CertificateException("Unable to validate certificate" +
+                        (certAlias == null ? "":" for alias [" +certAlias + "]") + ": " + kse.getMessage(), kse);
+            }
+            
+            validate(certChain);
+        } 
+    }
+    
+    public void validate(Certificate[] certChain) throws CertificateException
+    {
+        try
+        {
+            ArrayList<X509Certificate> certList = new ArrayList<X509Certificate>();
+            for (Certificate item : certChain)
+            {
+                if (item == null)
+                    continue;
+                
+                if (!(item instanceof X509Certificate))
+                {
+                    throw new IllegalStateException("Invalid certificate type in chain");
+                }
+                
+                certList.add((X509Certificate)item);
+            }
+    
+            if (certList.isEmpty())
+            {
+                throw new IllegalStateException("Invalid certificate chain");
+                
+            }
+    
+            X509CertSelector certSelect = new X509CertSelector();
+            certSelect.setCertificate(certList.get(0));
+            
+            // Configure certification path builder parameters
+            PKIXBuilderParameters pbParams = new PKIXBuilderParameters(_trustStore, certSelect);
+            pbParams.addCertStore(CertStore.getInstance("Collection", new CollectionCertStoreParameters(certList)));
+    
+            // Set maximum certification path length
+            pbParams.setMaxPathLength(_maxCertPathLength);
+    
+            // Enable revocation checking
+            pbParams.setRevocationEnabled(true);
+    
+            // Set static Certificate Revocation List
+            if (_crls != null && !_crls.isEmpty())
+            {
+                pbParams.addCertStore(CertStore.getInstance("Collection", new CollectionCertStoreParameters(_crls)));
+            }
+    
+            // Enable On-Line Certificate Status Protocol (OCSP) support
+            if (_enableOCSP)
+            {
+                Security.setProperty("ocsp.enable","true");
+            }
+            // Enable Certificate Revocation List Distribution Points (CRLDP) support
+            if (_enableCRLDP)
+            {
+                System.setProperty("com.sun.security.enableCRLDP","true");
+            }
+    
+            // Build certification path
+            CertPathBuilderResult buildResult = CertPathBuilder.getInstance("PKIX").build(pbParams);               
+            
+            // Validate certification path
+            CertPathValidator.getInstance("PKIX").validate(buildResult.getCertPath(),pbParams);
+        }
+        catch (GeneralSecurityException gse)
+        {
+            LOG.debug(gse);
+            throw new CertificateException("Unable to validate certificate: " + gse.getMessage(), gse);
+        }
+    }
+
+    public KeyStore getTrustStore()
+    {
+        return _trustStore;
+    }
+
+    public Collection<? extends CRL> getCrls()
+    {
+        return _crls;
+    }
+
+    /**
+     * @return Maximum number of intermediate certificates in
+     * the certification path (-1 for unlimited)
+     */
+    public int getMaxCertPathLength()
+    {
+        return _maxCertPathLength;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param maxCertPathLength
+     *            maximum number of intermediate certificates in
+     *            the certification path (-1 for unlimited)
+     */
+    public void setMaxCertPathLength(int maxCertPathLength)
+    {
+        _maxCertPathLength = maxCertPathLength;
+    }
+    
+    /* ------------------------------------------------------------ */
+    /** 
+     * @return true if CRL Distribution Points support is enabled
+     */
+    public boolean isEnableCRLDP()
+    {
+        return _enableCRLDP;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Enables CRL Distribution Points Support
+     * @param enableCRLDP true - turn on, false - turns off
+     */
+    public void setEnableCRLDP(boolean enableCRLDP)
+    {
+        _enableCRLDP = enableCRLDP;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** 
+     * @return true if On-Line Certificate Status Protocol support is enabled
+     */
+    public boolean isEnableOCSP()
+    {
+        return _enableOCSP;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Enables On-Line Certificate Status Protocol support
+     * @param enableOCSP true - turn on, false - turn off
+     */
+    public void setEnableOCSP(boolean enableOCSP)
+    {
+        _enableOCSP = enableOCSP;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** 
+     * @return Location of the OCSP Responder
+     */
+    public String getOcspResponderURL()
+    {
+        return _ocspResponderURL;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set the location of the OCSP Responder.
+     * @param ocspResponderURL location of the OCSP Responder
+     */
+    public void setOcspResponderURL(String ocspResponderURL)
+    {
+        _ocspResponderURL = ocspResponderURL;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/security/Constraint.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,226 @@
+//
+//  ========================================================================
+//  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.util.security;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+/* ------------------------------------------------------------ */
+/**
+ * Describe an auth and/or data constraint.
+ * 
+ * 
+ */
+public class Constraint implements Cloneable, Serializable
+{
+    /* ------------------------------------------------------------ */
+    public final static String __BASIC_AUTH = "BASIC";
+
+    public final static String __FORM_AUTH = "FORM";
+
+    public final static String __DIGEST_AUTH = "DIGEST";
+
+    public final static String __CERT_AUTH = "CLIENT_CERT";
+
+    public final static String __CERT_AUTH2 = "CLIENT-CERT";
+    
+    public final static String __SPNEGO_AUTH = "SPNEGO";
+    
+    public final static String __NEGOTIATE_AUTH = "NEGOTIATE";
+    
+    public static boolean validateMethod (String method)
+    {
+        if (method == null)
+            return false;
+        method = method.trim();
+        return (method.equals(__FORM_AUTH) 
+                || method.equals(__BASIC_AUTH) 
+                || method.equals (__DIGEST_AUTH) 
+                || method.equals (__CERT_AUTH) 
+                || method.equals(__CERT_AUTH2)
+                || method.equals(__SPNEGO_AUTH)
+                || method.equals(__NEGOTIATE_AUTH));
+    }
+
+    /* ------------------------------------------------------------ */
+    public final static int DC_UNSET = -1, DC_NONE = 0, DC_INTEGRAL = 1, DC_CONFIDENTIAL = 2, DC_FORBIDDEN = 3;
+
+    /* ------------------------------------------------------------ */
+    public final static String NONE = "NONE";
+
+    public final static String ANY_ROLE = "*";
+
+    /* ------------------------------------------------------------ */
+    private String _name;
+
+    private String[] _roles;
+
+    private int _dataConstraint = DC_UNSET;
+
+    private boolean _anyRole = false;
+
+    private boolean _authenticate = false;
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Constructor.
+     */
+    public Constraint()
+    {
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Conveniance Constructor.
+     * 
+     * @param name
+     * @param role
+     */
+    public Constraint(String name, String role)
+    {
+        setName(name);
+        setRoles(new String[] { role });
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public Object clone() throws CloneNotSupportedException
+    {
+        return super.clone();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param name
+     */
+    public void setName(String name)
+    {
+        _name = name;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setRoles(String[] roles)
+    {
+        _roles = roles;
+        _anyRole = false;
+        if (roles != null) 
+            for (int i = roles.length; !_anyRole && i-- > 0;)
+                _anyRole |= ANY_ROLE.equals(roles[i]);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return True if any user role is permitted.
+     */
+    public boolean isAnyRole()
+    {
+        return _anyRole;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return List of roles for this constraint.
+     */
+    public String[] getRoles()
+    {
+        return _roles;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param role
+     * @return True if the constraint contains the role.
+     */
+    public boolean hasRole(String role)
+    {
+        if (_anyRole) return true;
+        if (_roles != null) for (int i = _roles.length; i-- > 0;)
+            if (role.equals(_roles[i])) return true;
+        return false;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param authenticate True if users must be authenticated
+     */
+    public void setAuthenticate(boolean authenticate)
+    {
+        _authenticate = authenticate;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return True if the constraint requires request authentication
+     */
+    public boolean getAuthenticate()
+    {
+        return _authenticate;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return True if authentication required but no roles set
+     */
+    public boolean isForbidden()
+    {
+        return _authenticate && !_anyRole && (_roles == null || _roles.length == 0);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param c Data constrain indicator: 0=DC+NONE, 1=DC_INTEGRAL &
+     *                2=DC_CONFIDENTIAL
+     */
+    public void setDataConstraint(int c)
+    {
+        if (c < 0 || c > DC_CONFIDENTIAL) throw new IllegalArgumentException("Constraint out of range");
+        _dataConstraint = c;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Data constrain indicator: 0=DC+NONE, 1=DC_INTEGRAL &
+     *         2=DC_CONFIDENTIAL
+     */
+    public int getDataConstraint()
+    {
+        return _dataConstraint;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return True if a data constraint has been set.
+     */
+    public boolean hasDataConstraint()
+    {
+        return _dataConstraint >= DC_NONE;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public String toString()
+    {
+        return "SC{" + _name
+               + ","
+               + (_anyRole ? "*" : (_roles == null ? "-" : Arrays.asList(_roles).toString()))
+               + ","
+               + (_dataConstraint == DC_UNSET ? "DC_UNSET}" : (_dataConstraint == DC_NONE ? "NONE}" : (_dataConstraint == DC_INTEGRAL ? "INTEGRAL}" : "CONFIDENTIAL}")));
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/security/Credential.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,229 @@
+//
+//  ========================================================================
+//  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.util.security;
+
+import java.io.Serializable;
+import java.security.MessageDigest;
+
+import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.TypeUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/* ------------------------------------------------------------ */
+/**
+ * Credentials. The Credential class represents an abstract mechanism for
+ * checking authentication credentials. A credential instance either represents
+ * a secret, or some data that could only be derived from knowing the secret.
+ * <p>
+ * Often a Credential is related to a Password via a one way algorithm, so while
+ * a Password itself is a Credential, a UnixCrypt or MD5 digest of a a password
+ * is only a credential that can be checked against the password.
+ * <p>
+ * This class includes an implementation for unix Crypt an MD5 digest.
+ * 
+ * @see Password
+ * 
+ */
+public abstract class Credential implements Serializable
+{
+    private static final Logger LOG = Log.getLogger(Credential.class);
+
+    private static final long serialVersionUID = -7760551052768181572L;
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Check a credential
+     * 
+     * @param credentials The credential to check against. This may either be
+     *                another Credential object, a Password object or a String
+     *                which is interpreted by this credential.
+     * @return True if the credentials indicated that the shared secret is known
+     *         to both this Credential and the passed credential.
+     */
+    public abstract boolean check(Object credentials);
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Get a credential from a String. If the credential String starts with a
+     * known Credential type (eg "CRYPT:" or "MD5:" ) then a Credential of that
+     * type is returned. Else the credential is assumed to be a Password.
+     * 
+     * @param credential String representation of the credential
+     * @return A Credential or Password instance.
+     */
+    public static Credential getCredential(String credential)
+    {
+        if (credential.startsWith(Crypt.__TYPE)) return new Crypt(credential);
+        if (credential.startsWith(MD5.__TYPE)) return new MD5(credential);
+
+        return new Password(credential);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Unix Crypt Credentials
+     */
+    public static class Crypt extends Credential
+    {
+        private static final long serialVersionUID = -2027792997664744210L;
+
+        public static final String __TYPE = "CRYPT:";
+
+        private final String _cooked;
+
+        Crypt(String cooked)
+        {
+            _cooked = cooked.startsWith(Crypt.__TYPE) ? cooked.substring(__TYPE.length()) : cooked;
+        }
+
+        @Override
+        public boolean check(Object credentials)
+        {
+            if (credentials instanceof char[])
+                credentials=new String((char[])credentials);
+            if (!(credentials instanceof String) && !(credentials instanceof Password)) 
+                LOG.warn("Can't check " + credentials.getClass() + " against CRYPT");
+
+            String passwd = credentials.toString();
+            return _cooked.equals(UnixCrypt.crypt(passwd, _cooked));
+        }
+
+        public static String crypt(String user, String pw)
+        {
+            return "CRYPT:" + UnixCrypt.crypt(pw, user);
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * MD5 Credentials
+     */
+    public static class MD5 extends Credential
+    {
+        private static final long serialVersionUID = 5533846540822684240L;
+
+        public static final String __TYPE = "MD5:";
+
+        public static final Object __md5Lock = new Object();
+
+        private static MessageDigest __md;
+
+        private final byte[] _digest;
+
+        /* ------------------------------------------------------------ */
+        MD5(String digest)
+        {
+            digest = digest.startsWith(__TYPE) ? digest.substring(__TYPE.length()) : digest;
+            _digest = TypeUtil.parseBytes(digest, 16);
+        }
+
+        /* ------------------------------------------------------------ */
+        public byte[] getDigest()
+        {
+            return _digest;
+        }
+
+        /* ------------------------------------------------------------ */
+        @Override
+        public boolean check(Object credentials)
+        {
+            try
+            {
+                byte[] digest = null;
+
+                if (credentials instanceof char[])
+                    credentials=new String((char[])credentials);
+                if (credentials instanceof Password || credentials instanceof String)
+                {
+                    synchronized (__md5Lock)
+                    {
+                        if (__md == null) __md = MessageDigest.getInstance("MD5");
+                        __md.reset();
+                        __md.update(credentials.toString().getBytes(StringUtil.__ISO_8859_1));
+                        digest = __md.digest();
+                    }
+                    if (digest == null || digest.length != _digest.length) return false;
+                    for (int i = 0; i < digest.length; i++)
+                        if (digest[i] != _digest[i]) return false;
+                    return true;
+                }
+                else if (credentials instanceof MD5)
+                {
+                    MD5 md5 = (MD5) credentials;
+                    if (_digest.length != md5._digest.length) return false;
+                    for (int i = 0; i < _digest.length; i++)
+                        if (_digest[i] != md5._digest[i]) return false;
+                    return true;
+                }
+                else if (credentials instanceof Credential)
+                {
+                    // Allow credential to attempt check - i.e. this'll work
+                    // for DigestAuthModule$Digest credentials
+                    return ((Credential) credentials).check(this);
+                }
+                else
+                {
+                    LOG.warn("Can't check " + credentials.getClass() + " against MD5");
+                    return false;
+                }
+            }
+            catch (Exception e)
+            {
+                LOG.warn(e);
+                return false;
+            }
+        }
+
+        /* ------------------------------------------------------------ */
+        public static String digest(String password)
+        {
+            try
+            {
+                byte[] digest;
+                synchronized (__md5Lock)
+                {
+                    if (__md == null)
+                    {
+                        try
+                        {
+                            __md = MessageDigest.getInstance("MD5");
+                        }
+                        catch (Exception e)
+                        {
+                            LOG.warn(e);
+                            return null;
+                        }
+                    }
+
+                    __md.reset();
+                    __md.update(password.getBytes(StringUtil.__ISO_8859_1));
+                    digest = __md.digest();
+                }
+
+                return __TYPE + TypeUtil.toString(digest, 16);
+            }
+            catch (Exception e)
+            {
+                LOG.warn(e);
+                return null;
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/security/Password.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,257 @@
+//
+//  ========================================================================
+//  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.util.security;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/* ------------------------------------------------------------ */
+/**
+ * Password utility class.
+ * 
+ * This utility class gets a password or pass phrase either by:
+ * 
+ * <PRE>
+ *  + Password is set as a system property.
+ *  + The password is prompted for and read from standard input
+ *  + A program is run to get the password.
+ * </pre>
+ * 
+ * Passwords that begin with OBF: are de obfuscated. Passwords can be obfuscated
+ * by run org.eclipse.util.Password as a main class. Obfuscated password are
+ * required if a system needs to recover the full password (eg. so that it may
+ * be passed to another system). They are not secure, but prevent casual
+ * observation.
+ * <p>
+ * Passwords that begin with CRYPT: are oneway encrypted with UnixCrypt. The
+ * real password cannot be retrieved, but comparisons can be made to other
+ * passwords. A Crypt can be generated by running org.eclipse.util.UnixCrypt as
+ * a main class, passing password and then the username. Checksum passwords are
+ * a secure(ish) way to store passwords that only need to be checked rather than
+ * recovered. Note that it is not strong security - specially if simple
+ * passwords are used.
+ * 
+ * 
+ */
+public class Password extends Credential
+{
+    private static final Logger LOG = Log.getLogger(Password.class);
+
+    private static final long serialVersionUID = 5062906681431569445L;
+
+    public static final String __OBFUSCATE = "OBF:";
+
+    private String _pw;
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Constructor.
+     * 
+     * @param password The String password.
+     */
+    public Password(String password)
+    {
+        _pw = password;
+
+        // expand password
+        while (_pw != null && _pw.startsWith(__OBFUSCATE))
+            _pw = deobfuscate(_pw);
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public String toString()
+    {
+        return _pw;
+    }
+
+    /* ------------------------------------------------------------ */
+    public String toStarString()
+    {
+        return "*****************************************************".substring(0, _pw.length());
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public boolean check(Object credentials)
+    {
+        if (this == credentials) return true;
+
+        if (credentials instanceof Password) return credentials.equals(_pw);
+
+        if (credentials instanceof String) return credentials.equals(_pw);
+
+        if (credentials instanceof char[]) return Arrays.equals(_pw.toCharArray(), (char[]) credentials);
+
+        if (credentials instanceof Credential) return ((Credential) credentials).check(_pw);
+
+        return false;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public boolean equals(Object o)
+    {
+        if (this == o) 
+            return true;
+
+        if (null == o) 
+            return false;
+
+        if (o instanceof Password)
+        {
+            Password p = (Password) o;
+            //noinspection StringEquality
+            return p._pw == _pw || (null != _pw && _pw.equals(p._pw));
+        }
+
+        if (o instanceof String) 
+            return o.equals(_pw);
+
+        return false;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public int hashCode()
+    {
+        return null == _pw ? super.hashCode() : _pw.hashCode();
+    }
+
+    /* ------------------------------------------------------------ */
+    public static String obfuscate(String s)
+    {
+        StringBuilder buf = new StringBuilder();
+        byte[] b = s.getBytes();
+
+        buf.append(__OBFUSCATE);
+        for (int i = 0; i < b.length; i++)
+        {
+            byte b1 = b[i];
+            byte b2 = b[s.length() - (i + 1)];
+            int i1 = 127 + b1 + b2;
+            int i2 = 127 + b1 - b2;
+            int i0 = i1 * 256 + i2;
+            String x = Integer.toString(i0, 36);
+
+            switch (x.length())
+            {
+                case 1:
+                    buf.append('0');
+                    buf.append('0');
+                    buf.append('0');
+                    buf.append(x);
+                    break;
+                case 2:
+                    buf.append('0');
+                    buf.append('0');
+                    buf.append(x);
+                    break;
+                case 3:
+                    buf.append('0');
+                    buf.append(x);
+                    break;
+                default:
+                    buf.append(x);
+                    break;
+            }
+        }
+        return buf.toString();
+
+    }
+
+    /* ------------------------------------------------------------ */
+    public static String deobfuscate(String s)
+    {
+        if (s.startsWith(__OBFUSCATE)) s = s.substring(4);
+
+        byte[] b = new byte[s.length() / 2];
+        int l = 0;
+        for (int i = 0; i < s.length(); i += 4)
+        {
+            String x = s.substring(i, i + 4);
+            int i0 = Integer.parseInt(x, 36);
+            int i1 = (i0 / 256);
+            int i2 = (i0 % 256);
+            b[l++] = (byte) ((i1 + i2 - 254) / 2);
+        }
+
+        return new String(b, 0, l);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Get a password. A password is obtained by trying
+     * <UL>
+     * <LI>Calling <Code>System.getProperty(realm,dft)</Code>
+     * <LI>Prompting for a password
+     * <LI>Using promptDft if nothing was entered.
+     * </UL>
+     * 
+     * @param realm The realm name for the password, used as a SystemProperty
+     *                name.
+     * @param dft The default password.
+     * @param promptDft The default to use if prompting for the password.
+     * @return Password
+     */
+    public static Password getPassword(String realm, String dft, String promptDft)
+    {
+        String passwd = System.getProperty(realm, dft);
+        if (passwd == null || passwd.length() == 0)
+        {
+            try
+            {
+                System.out.print(realm + ((promptDft != null && promptDft.length() > 0) ? " [dft]" : "") + " : ");
+                System.out.flush();
+                byte[] buf = new byte[512];
+                int len = System.in.read(buf);
+                if (len > 0) passwd = new String(buf, 0, len).trim();
+            }
+            catch (IOException e)
+            {
+                LOG.warn(Log.EXCEPTION, e);
+            }
+            if (passwd == null || passwd.length() == 0) passwd = promptDft;
+        }
+        return new Password(passwd);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param arg
+     */
+    public static void main(String[] arg)
+    {
+        if (arg.length != 1 && arg.length != 2)
+        {
+            System.err.println("Usage - java org.eclipse.jetty.security.Password [<user>] <password>");
+            System.err.println("If the password is ?, the user will be prompted for the password");
+            System.exit(1);
+        }
+        String p = arg[arg.length == 1 ? 0 : 1];
+        Password pw = new Password(p);
+        System.err.println(pw.toString());
+        System.err.println(obfuscate(pw.toString()));
+        System.err.println(Credential.MD5.digest(p));
+        if (arg.length == 2) System.err.println(Credential.Crypt.crypt(arg[0], pw.toString()));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/security/UnixCrypt.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,461 @@
+/*
+ * @(#)UnixCrypt.java	0.9 96/11/25
+ *
+ * Copyright (c) 1996 Aki Yoshida. All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software
+ * for non-commercial or commercial purposes and without fee is
+ * hereby granted provided that this copyright notice appears in
+ * all copies.
+ */
+
+/**
+ * Unix crypt(3C) utility
+ *
+ * @version 	0.9, 11/25/96
+ * @author 	Aki Yoshida
+ */
+
+/**
+ * modified April 2001
+ * by Iris Van den Broeke, Daniel Deville
+ */
+
+package org.eclipse.jetty.util.security;
+
+
+/* ------------------------------------------------------------ */
+/**
+ * Unix Crypt. Implements the one way cryptography used by Unix systems for
+ * simple password protection.
+ * 
+ * @version $Id: UnixCrypt.java,v 1.1 2005/10/05 14:09:14 janb Exp $
+ * @author Greg Wilkins (gregw)
+ */
+public class UnixCrypt
+{
+
+    /* (mostly) Standard DES Tables from Tom Truscott */
+    private static final byte[] IP = { /* initial permutation */
+    58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, 62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8, 57, 49, 41, 33, 25, 17, 9, 1,
+            59, 51, 43, 35, 27, 19, 11, 3, 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7 };
+
+    /* The final permutation is the inverse of IP - no table is necessary */
+    private static final byte[] ExpandTr = { /* expansion operation */
+    32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9, 8, 9, 10, 11, 12, 13, 12, 13, 14, 15, 16, 17, 16, 17, 18, 19, 20, 21, 20, 21, 22, 23, 24, 25, 24, 25, 26, 27, 28, 29,
+            28, 29, 30, 31, 32, 1 };
+
+    private static final byte[] PC1 = { /* permuted choice table 1 */
+    57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36,
+
+    63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4 };
+
+    private static final byte[] Rotates = { /* PC1 rotation schedule */
+    1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 };
+
+    private static final byte[] PC2 = { /* permuted choice table 2 */
+    9, 18, 14, 17, 11, 24, 1, 5, 22, 25, 3, 28, 15, 6, 21, 10, 35, 38, 23, 19, 12, 4, 26, 8, 43, 54, 16, 7, 27, 20, 13, 2,
+
+    0, 0, 41, 52, 31, 37, 47, 55, 0, 0, 30, 40, 51, 45, 33, 48, 0, 0, 44, 49, 39, 56, 34, 53, 0, 0, 46, 42, 50, 36, 29, 32 };
+
+    private static final byte[][] S = { /* 48->32 bit substitution tables */
+            /* S[1] */
+            { 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9,
+             7, 3, 10, 5, 0, 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13 },
+            /* S[2] */
+            { 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12,
+             6, 9, 3, 2, 15, 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9 },
+            /* S[3] */
+            { 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2,
+             12, 5, 10, 14, 7, 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12 },
+            /* S[4] */
+            { 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3,
+             14, 5, 2, 8, 4, 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14 },
+            /* S[5] */
+            { 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12,
+             5, 6, 3, 0, 14, 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3 },
+            /* S[6] */
+            { 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4,
+             10, 1, 13, 11, 6, 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13 },
+            /* S[7] */
+            { 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, 1, 4, 11, 13, 12, 3, 7, 14, 10, 15,
+             6, 8, 0, 5, 9, 2, 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12 },
+            /* S[8] */
+            { 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10,
+             13, 15, 3, 5, 8, 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11 } };
+
+    private static final byte[] P32Tr = { /* 32-bit permutation function */
+    16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10, 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25 };
+
+    private static final byte[] CIFP = { /*
+                                             * compressed/interleaved
+                                             * permutation
+                                             */
+    1, 2, 3, 4, 17, 18, 19, 20, 5, 6, 7, 8, 21, 22, 23, 24, 9, 10, 11, 12, 25, 26, 27, 28, 13, 14, 15, 16, 29, 30, 31, 32,
+
+    33, 34, 35, 36, 49, 50, 51, 52, 37, 38, 39, 40, 53, 54, 55, 56, 41, 42, 43, 44, 57, 58, 59, 60, 45, 46, 47, 48, 61, 62, 63, 64 };
+
+    private static final byte[] ITOA64 = { /* 0..63 => ascii-64 */
+    (byte) '.', (byte) '/', (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) 'A',
+            (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M',
+            (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y',
+            (byte) 'Z', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k',
+            (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w',
+            (byte) 'x', (byte) 'y', (byte) 'z' };
+
+    /* ===== Tables that are initialized at run time ==================== */
+
+    private static final byte[] A64TOI = new byte[128]; /* ascii-64 => 0..63 */
+
+    /* Initial key schedule permutation */
+    private static final long[][] PC1ROT = new long[16][16];
+
+    /* Subsequent key schedule rotation permutations */
+    private static final long[][][] PC2ROT = new long[2][16][16];
+
+    /* Initial permutation/expansion table */
+    private static final long[][] IE3264 = new long[8][16];
+
+    /* Table that combines the S, P, and E operations. */
+    private static final long[][] SPE = new long[8][64];
+
+    /* compressed/interleaved => final permutation table */
+    private static final long[][] CF6464 = new long[16][16];
+
+    /* ==================================== */
+
+    static
+    {
+        byte[] perm = new byte[64];
+        byte[] temp = new byte[64];
+
+        // inverse table.
+        for (int i = 0; i < 64; i++)
+            A64TOI[ITOA64[i]] = (byte) i;
+
+        // PC1ROT - bit reverse, then PC1, then Rotate, then PC2
+        for (int i = 0; i < 64; i++)
+            perm[i] = (byte) 0;
+        
+        for (int i = 0; i < 64; i++)
+        {
+            int k;
+            if ((k = PC2[i]) == 0) continue;
+            k += Rotates[0] - 1;
+            if ((k % 28) < Rotates[0]) k -= 28;
+            k = PC1[k];
+            if (k > 0)
+            {
+                k--;
+                k = (k | 0x07) - (k & 0x07);
+                k++;
+            }
+            perm[i] = (byte) k;
+        }
+        init_perm(PC1ROT, perm, 8);
+
+        // PC2ROT - PC2 inverse, then Rotate, then PC2
+        for (int j = 0; j < 2; j++)
+        {
+            int k;
+            for (int i = 0; i < 64; i++)
+                perm[i] = temp[i] = 0;
+            for (int i = 0; i < 64; i++)
+            {
+                if ((k = PC2[i]) == 0) continue;
+                temp[k - 1] = (byte) (i + 1);
+            }
+            for (int i = 0; i < 64; i++)
+            {
+                if ((k = PC2[i]) == 0) continue;
+                k += j;
+                if ((k % 28) <= j) k -= 28;
+                perm[i] = temp[k];
+            }
+
+            init_perm(PC2ROT[j], perm, 8);
+        }
+
+        // Bit reverse, intial permupation, expantion
+        for (int i = 0; i < 8; i++)
+        {
+            for (int j = 0; j < 8; j++)
+            {
+                int k = (j < 2) ? 0 : IP[ExpandTr[i * 6 + j - 2] - 1];
+                if (k > 32)
+                    k -= 32;
+                else if (k > 0) k--;
+                if (k > 0)
+                {
+                    k--;
+                    k = (k | 0x07) - (k & 0x07);
+                    k++;
+                }
+                perm[i * 8 + j] = (byte) k;
+            }
+        }
+
+        init_perm(IE3264, perm, 8);
+
+        // Compression, final permutation, bit reverse
+        for (int i = 0; i < 64; i++)
+        {
+            int k = IP[CIFP[i] - 1];
+            if (k > 0)
+            {
+                k--;
+                k = (k | 0x07) - (k & 0x07);
+                k++;
+            }
+            perm[k - 1] = (byte) (i + 1);
+        }
+
+        init_perm(CF6464, perm, 8);
+
+        // SPE table
+        for (int i = 0; i < 48; i++)
+            perm[i] = P32Tr[ExpandTr[i] - 1];
+        for (int t = 0; t < 8; t++)
+        {
+            for (int j = 0; j < 64; j++)
+            {
+                int k = (((j >> 0) & 0x01) << 5) | (((j >> 1) & 0x01) << 3)
+                        | (((j >> 2) & 0x01) << 2)
+                        | (((j >> 3) & 0x01) << 1)
+                        | (((j >> 4) & 0x01) << 0)
+                        | (((j >> 5) & 0x01) << 4);
+                k = S[t][k];
+                k = (((k >> 3) & 0x01) << 0) | (((k >> 2) & 0x01) << 1) | (((k >> 1) & 0x01) << 2) | (((k >> 0) & 0x01) << 3);
+                for (int i = 0; i < 32; i++)
+                    temp[i] = 0;
+                for (int i = 0; i < 4; i++)
+                    temp[4 * t + i] = (byte) ((k >> i) & 0x01);
+                long kk = 0;
+                for (int i = 24; --i >= 0;)
+                    kk = ((kk << 1) | ((long) temp[perm[i] - 1]) << 32 | (temp[perm[i + 24] - 1]));
+
+                SPE[t][j] = to_six_bit(kk);
+            }
+        }
+    }
+
+    /**
+     * You can't call the constructer.
+     */
+    private UnixCrypt()
+    {
+    }
+
+    /**
+     * Returns the transposed and split code of a 24-bit code into a 4-byte
+     * code, each having 6 bits.
+     */
+    private static int to_six_bit(int num)
+    {
+        return (((num << 26) & 0xfc000000) | ((num << 12) & 0xfc0000) | ((num >> 2) & 0xfc00) | ((num >> 16) & 0xfc));
+    }
+
+    /**
+     * Returns the transposed and split code of two 24-bit code into two 4-byte
+     * code, each having 6 bits.
+     */
+    private static long to_six_bit(long num)
+    {
+        return (((num << 26) & 0xfc000000fc000000L) | ((num << 12) & 0xfc000000fc0000L) | ((num >> 2) & 0xfc000000fc00L) | ((num >> 16) & 0xfc000000fcL));
+    }
+
+    /**
+     * Returns the permutation of the given 64-bit code with the specified
+     * permutataion table.
+     */
+    private static long perm6464(long c, long[][] p)
+    {
+        long out = 0L;
+        for (int i = 8; --i >= 0;)
+        {
+            int t = (int) (0x00ff & c);
+            c >>= 8;
+            long tp = p[i << 1][t & 0x0f];
+            out |= tp;
+            tp = p[(i << 1) + 1][t >> 4];
+            out |= tp;
+        }
+        return out;
+    }
+
+    /**
+     * Returns the permutation of the given 32-bit code with the specified
+     * permutataion table.
+     */
+    private static long perm3264(int c, long[][] p)
+    {
+        long out = 0L;
+        for (int i = 4; --i >= 0;)
+        {
+            int t = (0x00ff & c);
+            c >>= 8;
+            long tp = p[i << 1][t & 0x0f];
+            out |= tp;
+            tp = p[(i << 1) + 1][t >> 4];
+            out |= tp;
+        }
+        return out;
+    }
+
+    /**
+     * Returns the key schedule for the given key.
+     */
+    private static long[] des_setkey(long keyword)
+    {
+        long K = perm6464(keyword, PC1ROT);
+        long[] KS = new long[16];
+        KS[0] = K & ~0x0303030300000000L;
+
+        for (int i = 1; i < 16; i++)
+        {
+            KS[i] = K;
+            K = perm6464(K, PC2ROT[Rotates[i] - 1]);
+
+            KS[i] = K & ~0x0303030300000000L;
+        }
+        return KS;
+    }
+
+    /**
+     * Returns the DES encrypted code of the given word with the specified
+     * environment.
+     */
+    private static long des_cipher(long in, int salt, int num_iter, long[] KS)
+    {
+        salt = to_six_bit(salt);
+        long L = in;
+        long R = L;
+        L &= 0x5555555555555555L;
+        R = (R & 0xaaaaaaaa00000000L) | ((R >> 1) & 0x0000000055555555L);
+        L = ((((L << 1) | (L << 32)) & 0xffffffff00000000L) | ((R | (R >> 32)) & 0x00000000ffffffffL));
+
+        L = perm3264((int) (L >> 32), IE3264);
+        R = perm3264((int) (L & 0xffffffff), IE3264);
+
+        while (--num_iter >= 0)
+        {
+            for (int loop_count = 0; loop_count < 8; loop_count++)
+            {
+                long kp;
+                long B;
+                long k;
+
+                kp = KS[(loop_count << 1)];
+                k = ((R >> 32) ^ R) & salt & 0xffffffffL;
+                k |= (k << 32);
+                B = (k ^ R ^ kp);
+
+                L ^= (SPE[0][(int) ((B >> 58) & 0x3f)] ^ SPE[1][(int) ((B >> 50) & 0x3f)]
+                      ^ SPE[2][(int) ((B >> 42) & 0x3f)]
+                      ^ SPE[3][(int) ((B >> 34) & 0x3f)]
+                      ^ SPE[4][(int) ((B >> 26) & 0x3f)]
+                      ^ SPE[5][(int) ((B >> 18) & 0x3f)]
+                      ^ SPE[6][(int) ((B >> 10) & 0x3f)] ^ SPE[7][(int) ((B >> 2) & 0x3f)]);
+
+                kp = KS[(loop_count << 1) + 1];
+                k = ((L >> 32) ^ L) & salt & 0xffffffffL;
+                k |= (k << 32);
+                B = (k ^ L ^ kp);
+
+                R ^= (SPE[0][(int) ((B >> 58) & 0x3f)] ^ SPE[1][(int) ((B >> 50) & 0x3f)]
+                      ^ SPE[2][(int) ((B >> 42) & 0x3f)]
+                      ^ SPE[3][(int) ((B >> 34) & 0x3f)]
+                      ^ SPE[4][(int) ((B >> 26) & 0x3f)]
+                      ^ SPE[5][(int) ((B >> 18) & 0x3f)]
+                      ^ SPE[6][(int) ((B >> 10) & 0x3f)] ^ SPE[7][(int) ((B >> 2) & 0x3f)]);
+            }
+            // swap L and R
+            L ^= R;
+            R ^= L;
+            L ^= R;
+        }
+        L = ((((L >> 35) & 0x0f0f0f0fL) | (((L & 0xffffffff) << 1) & 0xf0f0f0f0L)) << 32 | (((R >> 35) & 0x0f0f0f0fL) | (((R & 0xffffffff) << 1) & 0xf0f0f0f0L)));
+
+        L = perm6464(L, CF6464);
+
+        return L;
+    }
+
+    /**
+     * Initializes the given permutation table with the mapping table.
+     */
+    private static void init_perm(long[][] perm, byte[] p, int chars_out)
+    {
+        for (int k = 0; k < chars_out * 8; k++)
+        {
+
+            int l = p[k] - 1;
+            if (l < 0) continue;
+            int i = l >> 2;
+            l = 1 << (l & 0x03);
+            for (int j = 0; j < 16; j++)
+            {
+                int s = ((k & 0x07) + ((7 - (k >> 3)) << 3));
+                if ((j & l) != 0x00) perm[i][j] |= (1L << s);
+            }
+        }
+    }
+
+    /**
+     * Encrypts String into crypt (Unix) code.
+     * 
+     * @param key the key to be encrypted
+     * @param setting the salt to be used
+     * @return the encrypted String
+     */
+    public static String crypt(String key, String setting)
+    {
+        long constdatablock = 0L; /* encryption constant */
+        byte[] cryptresult = new byte[13]; /* encrypted result */
+        long keyword = 0L;
+        /* invalid parameters! */
+        if (key == null || setting == null) return "*"; // will NOT match under
+        // ANY circumstances!
+
+        int keylen = key.length();
+
+        for (int i = 0; i < 8; i++)
+        {
+            keyword = (keyword << 8) | ((i < keylen) ? 2 * key.charAt(i) : 0);
+        }
+
+        long[] KS = des_setkey(keyword);
+
+        int salt = 0;
+        for (int i = 2; --i >= 0;)
+        {
+            char c = (i < setting.length()) ? setting.charAt(i) : '.';
+            cryptresult[i] = (byte) c;
+            salt = (salt << 6) | (0x00ff & A64TOI[c]);
+        }
+
+        long rsltblock = des_cipher(constdatablock, salt, 25, KS);
+
+        cryptresult[12] = ITOA64[(((int) rsltblock) << 2) & 0x3f];
+        rsltblock >>= 4;
+        for (int i = 12; --i >= 2;)
+        {
+            cryptresult[i] = ITOA64[((int) rsltblock) & 0x3f];
+            rsltblock >>= 6;
+        }
+
+        return new String(cryptresult, 0, 13);
+    }
+
+    public static void main(String[] arg)
+    {
+        if (arg.length != 2)
+        {
+            System.err.println("Usage - java org.eclipse.util.UnixCrypt <key> <salt>");
+            System.exit(1);
+        }
+
+        System.err.println("Crypt=" + crypt(arg[0], arg[1]));
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/ssl/AliasedX509ExtendedKeyManager.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,130 @@
+//
+//  ========================================================================
+//  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.util.ssl;
+
+import java.net.Socket;
+import java.security.Principal;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.X509ExtendedKeyManager;
+import javax.net.ssl.X509KeyManager;
+
+
+/* ------------------------------------------------------------ */
+/**
+ * KeyManager to select a key with desired alias
+ * while delegating processing to specified KeyManager
+ * Can be used both with server and client sockets
+ */
+public class AliasedX509ExtendedKeyManager extends X509ExtendedKeyManager
+{
+    private String _keyAlias;
+    private X509KeyManager _keyManager;
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Construct KeyManager instance
+     * @param keyAlias Alias of the key to be selected
+     * @param keyManager Instance of KeyManager to be wrapped
+     * @throws Exception
+     */
+    public AliasedX509ExtendedKeyManager(String keyAlias, X509KeyManager keyManager) throws Exception
+    {
+        _keyAlias = keyAlias;
+        _keyManager = keyManager;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see javax.net.ssl.X509KeyManager#chooseClientAlias(java.lang.String[], java.security.Principal[], java.net.Socket)
+     */
+    public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket)
+    {
+        return _keyAlias == null ? _keyManager.chooseClientAlias(keyType, issuers, socket) : _keyAlias;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see javax.net.ssl.X509KeyManager#chooseServerAlias(java.lang.String, java.security.Principal[], java.net.Socket)
+     */
+    public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket)
+    {   
+        return _keyAlias == null ? _keyManager.chooseServerAlias(keyType, issuers, socket) : _keyAlias;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see javax.net.ssl.X509KeyManager#getClientAliases(java.lang.String, java.security.Principal[])
+     */
+    public String[] getClientAliases(String keyType, Principal[] issuers)
+    {
+        return _keyManager.getClientAliases(keyType, issuers);
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see javax.net.ssl.X509KeyManager#getServerAliases(java.lang.String, java.security.Principal[])
+     */
+    public String[] getServerAliases(String keyType, Principal[] issuers)
+    {
+        return _keyManager.getServerAliases(keyType, issuers);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see javax.net.ssl.X509KeyManager#getCertificateChain(java.lang.String)
+     */
+    public X509Certificate[] getCertificateChain(String alias)
+    {
+        return _keyManager.getCertificateChain(alias);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see javax.net.ssl.X509KeyManager#getPrivateKey(java.lang.String)
+     */
+    public PrivateKey getPrivateKey(String alias)
+    {
+        return _keyManager.getPrivateKey(alias);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see javax.net.ssl.X509ExtendedKeyManager#chooseEngineServerAlias(java.lang.String, java.security.Principal[], javax.net.ssl.SSLEngine)
+     */
+    @Override
+    public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine)
+    {
+        return _keyAlias == null ? super.chooseEngineServerAlias(keyType,issuers,engine) : _keyAlias;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see javax.net.ssl.X509ExtendedKeyManager#chooseEngineClientAlias(String[], Principal[], SSLEngine)
+     */
+    @Override
+    public String chooseEngineClientAlias(String keyType[], Principal[] issuers, SSLEngine engine)
+    {
+        return _keyAlias == null ? super.chooseEngineClientAlias(keyType,issuers,engine) : _keyAlias;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/ssl/AliasedX509KeyManager.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,107 @@
+//
+//  ========================================================================
+//  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.util.ssl;
+
+import java.net.Socket;
+import java.security.Principal;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.X509KeyManager;
+
+
+/* ------------------------------------------------------------ */
+/**
+ * KeyManager to select a key with desired alias
+ * while delegating processing to specified KeyManager
+ * Can be used both with server and client sockets
+ */
+public class AliasedX509KeyManager implements X509KeyManager
+{
+    private String _keyAlias;
+    private X509KeyManager _keyManager;
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Construct KeyManager instance
+     * @param keyAlias Alias of the key to be selected
+     * @param keyManager Instance of KeyManager to be wrapped
+     * @throws Exception
+     */
+    public AliasedX509KeyManager(String keyAlias, X509KeyManager keyManager) throws Exception
+    {
+        _keyAlias = keyAlias;
+        _keyManager = keyManager;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see javax.net.ssl.X509KeyManager#chooseClientAlias(java.lang.String[], java.security.Principal[], java.net.Socket)
+     */
+    public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket)
+    {
+        return _keyAlias == null ? _keyManager.chooseClientAlias(keyType, issuers, socket) : _keyAlias;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see javax.net.ssl.X509KeyManager#chooseServerAlias(java.lang.String, java.security.Principal[], java.net.Socket)
+     */
+    public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket)
+    {   
+        return _keyAlias == null ?_keyManager.chooseServerAlias(keyType, issuers, socket) : _keyAlias;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see javax.net.ssl.X509KeyManager#getClientAliases(java.lang.String, java.security.Principal[])
+     */
+    public String[] getClientAliases(String keyType, Principal[] issuers)
+    {
+        return _keyManager.getClientAliases(keyType, issuers);
+    }
+
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see javax.net.ssl.X509KeyManager#getServerAliases(java.lang.String, java.security.Principal[])
+     */
+    public String[] getServerAliases(String keyType, Principal[] issuers)
+    {
+        return _keyManager.getServerAliases(keyType, issuers);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see javax.net.ssl.X509KeyManager#getCertificateChain(java.lang.String)
+     */
+    public X509Certificate[] getCertificateChain(String alias)
+    {
+        return _keyManager.getCertificateChain(alias);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @see javax.net.ssl.X509KeyManager#getPrivateKey(java.lang.String)
+     */
+    public PrivateKey getPrivateKey(String alias)
+    {
+        return _keyManager.getPrivateKey(alias);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/ssl/SslContextFactory.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,1537 @@
+//
+//  ========================================================================
+//  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.util.ssl;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetAddress;
+import java.security.InvalidParameterException;
+import java.security.KeyStore;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.cert.CRL;
+import java.security.cert.CertStore;
+import java.security.cert.Certificate;
+import java.security.cert.CollectionCertStoreParameters;
+import java.security.cert.PKIXBuilderParameters;
+import java.security.cert.X509CertSelector;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import javax.net.ssl.CertPathTrustManagerParameters;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509KeyManager;
+import javax.net.ssl.X509TrustManager;
+
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.component.AbstractLifeCycle;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.resource.Resource;
+import org.eclipse.jetty.util.security.CertificateUtils;
+import org.eclipse.jetty.util.security.CertificateValidator;
+import org.eclipse.jetty.util.security.Password;
+
+
+/* ------------------------------------------------------------ */
+/**
+ * SslContextFactory is used to configure SSL connectors
+ * as well as HttpClient. It holds all SSL parameters and
+ * creates SSL context based on these parameters to be
+ * used by the SSL connectors.
+ */
+public class SslContextFactory extends AbstractLifeCycle
+{
+    public final static TrustManager[] TRUST_ALL_CERTS = new X509TrustManager[]{new X509TrustManager()
+    {
+        public java.security.cert.X509Certificate[] getAcceptedIssuers()
+        {
+            return new java.security.cert.X509Certificate[]{};
+        }
+
+        public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType)
+        {
+        }
+
+        public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType)
+        {
+        }
+    }};
+
+    private static final Logger LOG = Log.getLogger(SslContextFactory.class);
+
+    public static final String DEFAULT_KEYMANAGERFACTORY_ALGORITHM =
+        (Security.getProperty("ssl.KeyManagerFactory.algorithm") == null ?
+                "SunX509" : Security.getProperty("ssl.KeyManagerFactory.algorithm"));
+    public static final String DEFAULT_TRUSTMANAGERFACTORY_ALGORITHM =
+        (Security.getProperty("ssl.TrustManagerFactory.algorithm") == null ?
+                "SunX509" : Security.getProperty("ssl.TrustManagerFactory.algorithm"));
+
+    /** Default value for the keystore location path. */
+    public static final String DEFAULT_KEYSTORE_PATH =
+        System.getProperty("user.home") + File.separator + ".keystore";
+
+    /** String name of key password property. */
+    public static final String KEYPASSWORD_PROPERTY = "org.eclipse.jetty.ssl.keypassword";
+
+    /** String name of keystore password property. */
+    public static final String PASSWORD_PROPERTY = "org.eclipse.jetty.ssl.password";
+
+    /** Excluded protocols. */
+    private final Set<String> _excludeProtocols = new LinkedHashSet<String>();
+    /** Included protocols. */
+    private Set<String> _includeProtocols = null;
+
+    /** Excluded cipher suites. */
+    private final Set<String> _excludeCipherSuites = new LinkedHashSet<String>();
+    /** Included cipher suites. */
+    private Set<String> _includeCipherSuites = null;
+
+    /** Keystore path. */
+    private String _keyStorePath;
+    /** Keystore provider name */
+    private String _keyStoreProvider;
+    /** Keystore type */
+    private String _keyStoreType = "JKS";
+    /** Keystore input stream */
+    private InputStream _keyStoreInputStream;
+
+    /** SSL certificate alias */
+    private String _certAlias;
+
+    /** Truststore path */
+    private String _trustStorePath;
+    /** Truststore provider name */
+    private String _trustStoreProvider;
+    /** Truststore type */
+    private String _trustStoreType = "JKS";
+    /** Truststore input stream */
+    private InputStream _trustStoreInputStream;
+
+    /** Set to true if client certificate authentication is required */
+    private boolean _needClientAuth = false;
+    /** Set to true if client certificate authentication is desired */
+    private boolean _wantClientAuth = false;
+
+    /** Set to true if renegotiation is allowed */
+    private boolean _allowRenegotiate = true;
+
+    /** Keystore password */
+    private transient Password _keyStorePassword;
+    /** Key manager password */
+    private transient Password _keyManagerPassword;
+    /** Truststore password */
+    private transient Password _trustStorePassword;
+
+    /** SSL provider name */
+    private String _sslProvider;
+    /** SSL protocol name */
+    private String _sslProtocol = "TLS";
+
+    /** SecureRandom algorithm */
+    private String _secureRandomAlgorithm;
+    /** KeyManager factory algorithm */
+    private String _keyManagerFactoryAlgorithm = DEFAULT_KEYMANAGERFACTORY_ALGORITHM;
+    /** TrustManager factory algorithm */
+    private String _trustManagerFactoryAlgorithm = DEFAULT_TRUSTMANAGERFACTORY_ALGORITHM;
+
+    /** Set to true if SSL certificate validation is required */
+    private boolean _validateCerts;
+    /** Set to true if SSL certificate of the peer validation is required */
+    private boolean _validatePeerCerts;
+    /** Maximum certification path length (n - number of intermediate certs, -1 for unlimited) */
+    private int _maxCertPathLength = -1;
+    /** Path to file that contains Certificate Revocation List */
+    private String _crlPath;
+    /** Set to true to enable CRL Distribution Points (CRLDP) support */
+    private boolean _enableCRLDP = false;
+    /** Set to true to enable On-Line Certificate Status Protocol (OCSP) support */
+    private boolean _enableOCSP = false;
+    /** Location of OCSP Responder */
+    private String _ocspResponderURL;
+
+    /** SSL keystore */
+    private KeyStore _keyStore;
+    /** SSL truststore */
+    private KeyStore _trustStore;
+    /** Set to true to enable SSL Session caching */
+    private boolean _sessionCachingEnabled = true;
+    /** SSL session cache size */
+    private int _sslSessionCacheSize;
+    /** SSL session timeout */
+    private int _sslSessionTimeout;
+
+    /** SSL context */
+    private SSLContext _context;
+
+    private boolean _trustAll;
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Construct an instance of SslContextFactory
+     * Default constructor for use in XmlConfiguration files
+     */
+    public SslContextFactory()
+    {
+        _trustAll=true;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Construct an instance of SslContextFactory
+     * Default constructor for use in XmlConfiguration files
+     * @param trustAll whether to blindly trust all certificates
+     * @see #setTrustAll(boolean)
+     */
+    public SslContextFactory(boolean trustAll)
+    {
+        _trustAll=trustAll;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Construct an instance of SslContextFactory
+     * @param keyStorePath default keystore location
+     */
+    public SslContextFactory(String keyStorePath)
+    {
+        _keyStorePath = keyStorePath;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Create the SSLContext object and start the lifecycle
+     * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
+     */
+    @Override
+    protected void doStart() throws Exception
+    {
+        if (_context == null)
+        {
+            if (_keyStore==null && _keyStoreInputStream == null && _keyStorePath == null &&
+                _trustStore==null && _trustStoreInputStream == null && _trustStorePath == null )
+            {
+                TrustManager[] trust_managers=null;
+
+                if (_trustAll)
+                {
+                    LOG.debug("No keystore or trust store configured.  ACCEPTING UNTRUSTED CERTIFICATES!!!!!");
+                    // Create a trust manager that does not validate certificate chains
+                    trust_managers = TRUST_ALL_CERTS;
+                }
+
+                SecureRandom secureRandom = (_secureRandomAlgorithm == null)?null:SecureRandom.getInstance(_secureRandomAlgorithm);
+                _context = SSLContext.getInstance(_sslProtocol);
+                _context.init(null, trust_managers, secureRandom);
+            }
+            else
+            {
+                // verify that keystore and truststore
+                // parameters are set up correctly
+                checkKeyStore();
+
+                KeyStore keyStore = loadKeyStore();
+                KeyStore trustStore = loadTrustStore();
+
+                Collection<? extends CRL> crls = loadCRL(_crlPath);
+
+                if (_validateCerts && keyStore != null)
+                {
+                    if (_certAlias == null)
+                    {
+                        List<String> aliases = Collections.list(keyStore.aliases());
+                        _certAlias = aliases.size() == 1 ? aliases.get(0) : null;
+                    }
+
+                    Certificate cert = _certAlias == null?null:keyStore.getCertificate(_certAlias);
+                    if (cert == null)
+                    {
+                        throw new Exception("No certificate found in the keystore" + (_certAlias==null ? "":" for alias " + _certAlias));
+                    }
+
+                    CertificateValidator validator = new CertificateValidator(trustStore, crls);
+                    validator.setMaxCertPathLength(_maxCertPathLength);
+                    validator.setEnableCRLDP(_enableCRLDP);
+                    validator.setEnableOCSP(_enableOCSP);
+                    validator.setOcspResponderURL(_ocspResponderURL);
+                    validator.validate(keyStore, cert);
+                }
+
+                KeyManager[] keyManagers = getKeyManagers(keyStore);
+                TrustManager[] trustManagers = getTrustManagers(trustStore,crls);
+
+                SecureRandom secureRandom = (_secureRandomAlgorithm == null)?null:SecureRandom.getInstance(_secureRandomAlgorithm);
+                _context = (_sslProvider == null)?SSLContext.getInstance(_sslProtocol):SSLContext.getInstance(_sslProtocol,_sslProvider);
+                _context.init(keyManagers,trustManagers,secureRandom);
+
+                SSLEngine engine=newSslEngine();
+
+                LOG.info("Enabled Protocols {} of {}",Arrays.asList(engine.getEnabledProtocols()),Arrays.asList(engine.getSupportedProtocols()));
+                if (LOG.isDebugEnabled())
+                    LOG.debug("Enabled Ciphers   {} of {}",Arrays.asList(engine.getEnabledCipherSuites()),Arrays.asList(engine.getSupportedCipherSuites()));
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The array of protocol names to exclude from
+     * {@link SSLEngine#setEnabledProtocols(String[])}
+     */
+    public String[] getExcludeProtocols()
+    {
+        return _excludeProtocols.toArray(new String[_excludeProtocols.size()]);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param protocols
+     *            The array of protocol names to exclude from
+     *            {@link SSLEngine#setEnabledProtocols(String[])}
+     */
+    public void setExcludeProtocols(String... protocols)
+    {
+        checkNotStarted();
+
+        _excludeProtocols.clear();
+        _excludeProtocols.addAll(Arrays.asList(protocols));
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param protocol Protocol names to add to {@link SSLEngine#setEnabledProtocols(String[])}
+     */
+    public void addExcludeProtocols(String... protocol)
+    {
+        checkNotStarted();
+        _excludeProtocols.addAll(Arrays.asList(protocol));
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The array of protocol names to include in
+     * {@link SSLEngine#setEnabledProtocols(String[])}
+     */
+    public String[] getIncludeProtocols()
+    {
+        return _includeProtocols.toArray(new String[_includeProtocols.size()]);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param protocols
+     *            The array of protocol names to include in
+     *            {@link SSLEngine#setEnabledProtocols(String[])}
+     */
+    public void setIncludeProtocols(String... protocols)
+    {
+        checkNotStarted();
+
+        _includeProtocols = new LinkedHashSet<String>(Arrays.asList(protocols));
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The array of cipher suite names to exclude from
+     * {@link SSLEngine#setEnabledCipherSuites(String[])}
+     */
+    public String[] getExcludeCipherSuites()
+    {
+        return _excludeCipherSuites.toArray(new String[_excludeCipherSuites.size()]);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param cipherSuites
+     *            The array of cipher suite names to exclude from
+     *            {@link SSLEngine#setEnabledCipherSuites(String[])}
+     */
+    public void setExcludeCipherSuites(String... cipherSuites)
+    {
+        checkNotStarted();
+        _excludeCipherSuites.clear();
+        _excludeCipherSuites.addAll(Arrays.asList(cipherSuites));
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param cipher Cipher names to add to {@link SSLEngine#setEnabledCipherSuites(String[])}
+     */
+    public void addExcludeCipherSuites(String... cipher)
+    {
+        checkNotStarted();
+        _excludeCipherSuites.addAll(Arrays.asList(cipher));
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The array of cipher suite names to include in
+     * {@link SSLEngine#setEnabledCipherSuites(String[])}
+     */
+    public String[] getIncludeCipherSuites()
+    {
+        return _includeCipherSuites.toArray(new String[_includeCipherSuites.size()]);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param cipherSuites
+     *            The array of cipher suite names to include in
+     *            {@link SSLEngine#setEnabledCipherSuites(String[])}
+     */
+    public void setIncludeCipherSuites(String... cipherSuites)
+    {
+        checkNotStarted();
+
+        _includeCipherSuites = new LinkedHashSet<String>(Arrays.asList(cipherSuites));
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The file or URL of the SSL Key store.
+     */
+    public String getKeyStorePath()
+    {
+        return _keyStorePath;
+    }
+
+    /* ------------------------------------------------------------ */
+    @Deprecated
+    public String getKeyStore()
+    {
+        return _keyStorePath;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param keyStorePath
+     *            The file or URL of the SSL Key store.
+     */
+    public void setKeyStorePath(String keyStorePath)
+    {
+        checkNotStarted();
+
+        _keyStorePath = keyStorePath;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param keyStorePath the file system path or URL of the keystore
+     * @deprecated Use {@link #setKeyStorePath(String)}
+     */
+    @Deprecated
+    public void setKeyStore(String keyStorePath)
+    {
+        checkNotStarted();
+
+        _keyStorePath = keyStorePath;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The provider of the key store
+     */
+    public String getKeyStoreProvider()
+    {
+        return _keyStoreProvider;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param keyStoreProvider
+     *            The provider of the key store
+     */
+    public void setKeyStoreProvider(String keyStoreProvider)
+    {
+        checkNotStarted();
+
+        _keyStoreProvider = keyStoreProvider;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The type of the key store (default "JKS")
+     */
+    public String getKeyStoreType()
+    {
+        return (_keyStoreType);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param keyStoreType
+     *            The type of the key store (default "JKS")
+     */
+    public void setKeyStoreType(String keyStoreType)
+    {
+        checkNotStarted();
+
+        _keyStoreType = keyStoreType;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Get the _keyStoreInputStream.
+     * @return the _keyStoreInputStream
+     *
+     * @deprecated
+     */
+    @Deprecated
+    public InputStream getKeyStoreInputStream()
+    {
+        checkKeyStore();
+
+        return _keyStoreInputStream;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set the keyStoreInputStream.
+     * @param keyStoreInputStream the InputStream to the KeyStore
+     *
+     * @deprecated Use {@link #setKeyStore(KeyStore)}
+     */
+    @Deprecated
+    public void setKeyStoreInputStream(InputStream keyStoreInputStream)
+    {
+        checkNotStarted();
+
+        _keyStoreInputStream = keyStoreInputStream;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Alias of SSL certificate for the connector
+     */
+    public String getCertAlias()
+    {
+        return _certAlias;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param certAlias
+     *            Alias of SSL certificate for the connector
+     */
+    public void setCertAlias(String certAlias)
+    {
+        checkNotStarted();
+
+        _certAlias = certAlias;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The file name or URL of the trust store location
+     */
+    public String getTrustStore()
+    {
+        return _trustStorePath;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param trustStorePath
+     *            The file name or URL of the trust store location
+     */
+    public void setTrustStore(String trustStorePath)
+    {
+        checkNotStarted();
+
+        _trustStorePath = trustStorePath;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The provider of the trust store
+     */
+    public String getTrustStoreProvider()
+    {
+        return _trustStoreProvider;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param trustStoreProvider
+     *            The provider of the trust store
+     */
+    public void setTrustStoreProvider(String trustStoreProvider)
+    {
+        checkNotStarted();
+
+        _trustStoreProvider = trustStoreProvider;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The type of the trust store (default "JKS")
+     */
+    public String getTrustStoreType()
+    {
+        return _trustStoreType;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param trustStoreType
+     *            The type of the trust store (default "JKS")
+     */
+    public void setTrustStoreType(String trustStoreType)
+    {
+        checkNotStarted();
+
+        _trustStoreType = trustStoreType;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Get the _trustStoreInputStream.
+     * @return the _trustStoreInputStream
+     *
+     * @deprecated
+     */
+    @Deprecated
+    public InputStream getTrustStoreInputStream()
+    {
+        checkKeyStore();
+
+        return _trustStoreInputStream;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set the _trustStoreInputStream.
+     * @param trustStoreInputStream the InputStream to the TrustStore
+     *
+     * @deprecated
+     */
+    @Deprecated
+    public void setTrustStoreInputStream(InputStream trustStoreInputStream)
+    {
+        checkNotStarted();
+
+        _trustStoreInputStream = trustStoreInputStream;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return True if SSL needs client authentication.
+     * @see SSLEngine#getNeedClientAuth()
+     */
+    public boolean getNeedClientAuth()
+    {
+        return _needClientAuth;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param needClientAuth
+     *            True if SSL needs client authentication.
+     * @see SSLEngine#getNeedClientAuth()
+     */
+    public void setNeedClientAuth(boolean needClientAuth)
+    {
+        checkNotStarted();
+
+        _needClientAuth = needClientAuth;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return True if SSL wants client authentication.
+     * @see SSLEngine#getWantClientAuth()
+     */
+    public boolean getWantClientAuth()
+    {
+        return _wantClientAuth;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param wantClientAuth
+     *            True if SSL wants client authentication.
+     * @see SSLEngine#getWantClientAuth()
+     */
+    public void setWantClientAuth(boolean wantClientAuth)
+    {
+        checkNotStarted();
+
+        _wantClientAuth = wantClientAuth;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return true if SSL certificate has to be validated
+     * @deprecated
+     */
+    @Deprecated
+    public boolean getValidateCerts()
+    {
+        return _validateCerts;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return true if SSL certificate has to be validated
+     */
+    public boolean isValidateCerts()
+    {
+        return _validateCerts;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param validateCerts
+     *            true if SSL certificates have to be validated
+     */
+    public void setValidateCerts(boolean validateCerts)
+    {
+        checkNotStarted();
+
+        _validateCerts = validateCerts;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return true if SSL certificates of the peer have to be validated
+     */
+    public boolean isValidatePeerCerts()
+    {
+        return _validatePeerCerts;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param validatePeerCerts
+     *            true if SSL certificates of the peer have to be validated
+     */
+    public void setValidatePeerCerts(boolean validatePeerCerts)
+    {
+        checkNotStarted();
+
+        _validatePeerCerts = validatePeerCerts;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return True if SSL re-negotiation is allowed (default false)
+     */
+    public boolean isAllowRenegotiate()
+    {
+        return _allowRenegotiate;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Set if SSL re-negotiation is allowed. CVE-2009-3555 discovered
+     * a vulnerability in SSL/TLS with re-negotiation.  If your JVM
+     * does not have CVE-2009-3555 fixed, then re-negotiation should
+     * not be allowed.  CVE-2009-3555 was fixed in Sun java 1.6 with a ban
+     * of renegotiates in u19 and with RFC5746 in u22.
+     *
+     * @param allowRenegotiate
+     *            true if re-negotiation is allowed (default false)
+     */
+    public void setAllowRenegotiate(boolean allowRenegotiate)
+    {
+        checkNotStarted();
+
+        _allowRenegotiate = allowRenegotiate;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param password
+     *            The password for the key store
+     */
+    public void setKeyStorePassword(String password)
+    {
+        checkNotStarted();
+
+        _keyStorePassword = Password.getPassword(PASSWORD_PROPERTY,password,null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param password
+     *            The password (if any) for the specific key within the key store
+     */
+    public void setKeyManagerPassword(String password)
+    {
+        checkNotStarted();
+
+        _keyManagerPassword = Password.getPassword(KEYPASSWORD_PROPERTY,password,null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param password
+     *            The password for the trust store
+     */
+    public void setTrustStorePassword(String password)
+    {
+        checkNotStarted();
+
+        _trustStorePassword = Password.getPassword(PASSWORD_PROPERTY,password,null);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The SSL provider name, which if set is passed to
+     * {@link SSLContext#getInstance(String, String)}
+     */
+    public String getProvider()
+    {
+        return _sslProvider;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param provider
+     *            The SSL provider name, which if set is passed to
+     *            {@link SSLContext#getInstance(String, String)}
+     */
+    public void setProvider(String provider)
+    {
+        checkNotStarted();
+
+        _sslProvider = provider;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The SSL protocol (default "TLS") passed to
+     * {@link SSLContext#getInstance(String, String)}
+     */
+    public String getProtocol()
+    {
+        return _sslProtocol;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param protocol
+     *            The SSL protocol (default "TLS") passed to
+     *            {@link SSLContext#getInstance(String, String)}
+     */
+    public void setProtocol(String protocol)
+    {
+        checkNotStarted();
+
+        _sslProtocol = protocol;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The algorithm name, which if set is passed to
+     * {@link SecureRandom#getInstance(String)} to obtain the {@link SecureRandom} instance passed to
+     * {@link SSLContext#init(javax.net.ssl.KeyManager[], javax.net.ssl.TrustManager[], SecureRandom)}
+     */
+    public String getSecureRandomAlgorithm()
+    {
+        return _secureRandomAlgorithm;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param algorithm
+     *            The algorithm name, which if set is passed to
+     *            {@link SecureRandom#getInstance(String)} to obtain the {@link SecureRandom} instance passed to
+     *            {@link SSLContext#init(javax.net.ssl.KeyManager[], javax.net.ssl.TrustManager[], SecureRandom)}
+     */
+    public void setSecureRandomAlgorithm(String algorithm)
+    {
+        checkNotStarted();
+
+        _secureRandomAlgorithm = algorithm;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The algorithm name (default "SunX509") used by the {@link KeyManagerFactory}
+     */
+    public String getSslKeyManagerFactoryAlgorithm()
+    {
+        return (_keyManagerFactoryAlgorithm);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param algorithm
+     *            The algorithm name (default "SunX509") used by the {@link KeyManagerFactory}
+     */
+    public void setSslKeyManagerFactoryAlgorithm(String algorithm)
+    {
+        checkNotStarted();
+
+        _keyManagerFactoryAlgorithm = algorithm;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The algorithm name (default "SunX509") used by the {@link TrustManagerFactory}
+     */
+    public String getTrustManagerFactoryAlgorithm()
+    {
+        return (_trustManagerFactoryAlgorithm);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return True if all certificates should be trusted if there is no KeyStore or TrustStore
+     */
+    public boolean isTrustAll()
+    {
+        return _trustAll;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param trustAll True if all certificates should be trusted if there is no KeyStore or TrustStore
+     */
+    public void setTrustAll(boolean trustAll)
+    {
+        _trustAll = trustAll;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param algorithm
+     *            The algorithm name (default "SunX509") used by the {@link TrustManagerFactory}
+     *            Use the string "TrustAll" to install a trust manager that trusts all.
+     */
+    public void setTrustManagerFactoryAlgorithm(String algorithm)
+    {
+        checkNotStarted();
+
+        _trustManagerFactoryAlgorithm = algorithm;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Path to file that contains Certificate Revocation List
+     */
+    public String getCrlPath()
+    {
+        return _crlPath;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param crlPath
+     *            Path to file that contains Certificate Revocation List
+     */
+    public void setCrlPath(String crlPath)
+    {
+        checkNotStarted();
+
+        _crlPath = crlPath;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Maximum number of intermediate certificates in
+     * the certification path (-1 for unlimited)
+     */
+    public int getMaxCertPathLength()
+    {
+        return _maxCertPathLength;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param maxCertPathLength
+     *            maximum number of intermediate certificates in
+     *            the certification path (-1 for unlimited)
+     */
+    public void setMaxCertPathLength(int maxCertPathLength)
+    {
+        checkNotStarted();
+
+        _maxCertPathLength = maxCertPathLength;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The SSLContext
+     */
+    public SSLContext getSslContext()
+    {
+        if (!isStarted())
+            throw new IllegalStateException(getState());
+        return _context;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param sslContext
+     *            Set a preconfigured SSLContext
+     */
+    public void setSslContext(SSLContext sslContext)
+    {
+        checkNotStarted();
+
+        _context = sslContext;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Override this method to provide alternate way to load a keystore.
+     *
+     * @return the key store instance
+     * @throws Exception if the keystore cannot be loaded
+     */
+    protected KeyStore loadKeyStore() throws Exception
+    {
+        return _keyStore != null ? _keyStore : getKeyStore(_keyStoreInputStream,
+                _keyStorePath, _keyStoreType, _keyStoreProvider,
+                _keyStorePassword==null? null: _keyStorePassword.toString());
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Override this method to provide alternate way to load a truststore.
+     *
+     * @return the key store instance
+     * @throws Exception if the truststore cannot be loaded
+     */
+    protected KeyStore loadTrustStore() throws Exception
+    {
+        return _trustStore != null ? _trustStore : getKeyStore(_trustStoreInputStream,
+                _trustStorePath, _trustStoreType,  _trustStoreProvider,
+                _trustStorePassword==null? null: _trustStorePassword.toString());
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Loads keystore using an input stream or a file path in the same
+     * order of precedence.
+     *
+     * Required for integrations to be able to override the mechanism
+     * used to load a keystore in order to provide their own implementation.
+     *
+     * @param storeStream keystore input stream
+     * @param storePath path of keystore file
+     * @param storeType keystore type
+     * @param storeProvider keystore provider
+     * @param storePassword keystore password
+     * @return created keystore
+     * @throws Exception if the keystore cannot be obtained
+     *
+     * @deprecated
+     */
+    @Deprecated
+    protected KeyStore getKeyStore(InputStream storeStream, String storePath, String storeType, String storeProvider, String storePassword) throws Exception
+    {
+        return CertificateUtils.getKeyStore(storeStream, storePath, storeType, storeProvider, storePassword);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Loads certificate revocation list (CRL) from a file.
+     *
+     * Required for integrations to be able to override the mechanism used to
+     * load CRL in order to provide their own implementation.
+     *
+     * @param crlPath path of certificate revocation list file
+     * @return Collection of CRL's
+     * @throws Exception if the certificate revocation list cannot be loaded
+     */
+    protected Collection<? extends CRL> loadCRL(String crlPath) throws Exception
+    {
+        return CertificateUtils.loadCRL(crlPath);
+    }
+
+    /* ------------------------------------------------------------ */
+    protected KeyManager[] getKeyManagers(KeyStore keyStore) throws Exception
+    {
+        KeyManager[] managers = null;
+
+        if (keyStore != null)
+        {
+            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(_keyManagerFactoryAlgorithm);
+            keyManagerFactory.init(keyStore,_keyManagerPassword == null?(_keyStorePassword == null?null:_keyStorePassword.toString().toCharArray()):_keyManagerPassword.toString().toCharArray());
+            managers = keyManagerFactory.getKeyManagers();
+
+            if (_certAlias != null)
+            {
+                for (int idx = 0; idx < managers.length; idx++)
+                {
+                    if (managers[idx] instanceof X509KeyManager)
+                    {
+                        managers[idx] = new AliasedX509ExtendedKeyManager(_certAlias,(X509KeyManager)managers[idx]);
+                    }
+                }
+            }
+        }
+
+        return managers;
+    }
+
+    /* ------------------------------------------------------------ */
+    protected TrustManager[] getTrustManagers(KeyStore trustStore, Collection<? extends CRL> crls) throws Exception
+    {
+        TrustManager[] managers = null;
+        if (trustStore != null)
+        {
+            // Revocation checking is only supported for PKIX algorithm
+            if (_validatePeerCerts && _trustManagerFactoryAlgorithm.equalsIgnoreCase("PKIX"))
+            {
+                PKIXBuilderParameters pbParams = new PKIXBuilderParameters(trustStore,new X509CertSelector());
+
+                // Set maximum certification path length
+                pbParams.setMaxPathLength(_maxCertPathLength);
+
+                // Make sure revocation checking is enabled
+                pbParams.setRevocationEnabled(true);
+
+                if (crls != null && !crls.isEmpty())
+                {
+                    pbParams.addCertStore(CertStore.getInstance("Collection",new CollectionCertStoreParameters(crls)));
+                }
+
+                if (_enableCRLDP)
+                {
+                    // Enable Certificate Revocation List Distribution Points (CRLDP) support
+                    System.setProperty("com.sun.security.enableCRLDP","true");
+                }
+
+                if (_enableOCSP)
+                {
+                    // Enable On-Line Certificate Status Protocol (OCSP) support
+                    Security.setProperty("ocsp.enable","true");
+
+                    if (_ocspResponderURL != null)
+                    {
+                        // Override location of OCSP Responder
+                        Security.setProperty("ocsp.responderURL", _ocspResponderURL);
+                    }
+                }
+
+                TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(_trustManagerFactoryAlgorithm);
+                trustManagerFactory.init(new CertPathTrustManagerParameters(pbParams));
+
+                managers = trustManagerFactory.getTrustManagers();
+            }
+            else
+            {
+                TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(_trustManagerFactoryAlgorithm);
+                trustManagerFactory.init(trustStore);
+
+                managers = trustManagerFactory.getTrustManagers();
+            }
+        }
+
+        return managers;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Check KeyStore Configuration. Ensures that if keystore has been
+     * configured but there's no truststore, that keystore is
+     * used as truststore.
+     * @throws IllegalStateException if SslContextFactory configuration can't be used.
+     */
+    public void checkKeyStore()
+    {
+        if (_context != null)
+            return; //nothing to check if using preconfigured context
+
+
+        if (_keyStore == null && _keyStoreInputStream == null && _keyStorePath == null)
+            throw new IllegalStateException("SSL doesn't have a valid keystore");
+
+        // if the keystore has been configured but there is no
+        // truststore configured, use the keystore as the truststore
+        if (_trustStore == null && _trustStoreInputStream == null && _trustStorePath == null)
+        {
+            _trustStore = _keyStore;
+            _trustStorePath = _keyStorePath;
+            _trustStoreInputStream = _keyStoreInputStream;
+            _trustStoreType = _keyStoreType;
+            _trustStoreProvider = _keyStoreProvider;
+            _trustStorePassword = _keyStorePassword;
+            _trustManagerFactoryAlgorithm = _keyManagerFactoryAlgorithm;
+        }
+
+        // It's the same stream we cannot read it twice, so read it once in memory
+        if (_keyStoreInputStream != null && _keyStoreInputStream == _trustStoreInputStream)
+        {
+            try
+            {
+                ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                IO.copy(_keyStoreInputStream, baos);
+                _keyStoreInputStream.close();
+
+                _keyStoreInputStream = new ByteArrayInputStream(baos.toByteArray());
+                _trustStoreInputStream = new ByteArrayInputStream(baos.toByteArray());
+            }
+            catch (Exception ex)
+            {
+                throw new IllegalStateException(ex);
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Select protocols to be used by the connector
+     * based on configured inclusion and exclusion lists
+     * as well as enabled and supported protocols.
+     * @param enabledProtocols Array of enabled protocols
+     * @param supportedProtocols Array of supported protocols
+     * @return Array of protocols to enable
+     */
+    public String[] selectProtocols(String[] enabledProtocols, String[] supportedProtocols)
+    {
+        Set<String> selected_protocols = new LinkedHashSet<String>();
+
+        // Set the starting protocols - either from the included or enabled list
+        if (_includeProtocols!=null)
+        {
+            // Use only the supported included protocols
+            for (String protocol : _includeProtocols)
+                if(Arrays.asList(supportedProtocols).contains(protocol))
+                    selected_protocols.add(protocol);
+        }
+        else
+            selected_protocols.addAll(Arrays.asList(enabledProtocols));
+
+
+        // Remove any excluded protocols
+        if (_excludeProtocols != null)
+            selected_protocols.removeAll(_excludeProtocols);
+
+        return selected_protocols.toArray(new String[selected_protocols.size()]);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Select cipher suites to be used by the connector
+     * based on configured inclusion and exclusion lists
+     * as well as enabled and supported cipher suite lists.
+     * @param enabledCipherSuites Array of enabled cipher suites
+     * @param supportedCipherSuites Array of supported cipher suites
+     * @return Array of cipher suites to enable
+     */
+    public String[] selectCipherSuites(String[] enabledCipherSuites, String[] supportedCipherSuites)
+    {
+        Set<String> selected_ciphers = new LinkedHashSet<String>();
+
+        // Set the starting ciphers - either from the included or enabled list
+        if (_includeCipherSuites!=null)
+        {
+            // Use only the supported included ciphers
+            for (String cipherSuite : _includeCipherSuites)
+                if(Arrays.asList(supportedCipherSuites).contains(cipherSuite))
+                    selected_ciphers.add(cipherSuite);
+        }
+        else
+            selected_ciphers.addAll(Arrays.asList(enabledCipherSuites));
+
+
+        // Remove any excluded ciphers
+        if (_excludeCipherSuites != null)
+            selected_ciphers.removeAll(_excludeCipherSuites);
+        return selected_ciphers.toArray(new String[selected_ciphers.size()]);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Check if the lifecycle has been started and throw runtime exception
+     */
+    protected void checkNotStarted()
+    {
+        if (isStarted())
+            throw new IllegalStateException("Cannot modify configuration when "+getState());
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return true if CRL Distribution Points support is enabled
+     */
+    public boolean isEnableCRLDP()
+    {
+        return _enableCRLDP;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Enables CRL Distribution Points Support
+     * @param enableCRLDP true - turn on, false - turns off
+     */
+    public void setEnableCRLDP(boolean enableCRLDP)
+    {
+        checkNotStarted();
+
+        _enableCRLDP = enableCRLDP;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return true if On-Line Certificate Status Protocol support is enabled
+     */
+    public boolean isEnableOCSP()
+    {
+        return _enableOCSP;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Enables On-Line Certificate Status Protocol support
+     * @param enableOCSP true - turn on, false - turn off
+     */
+    public void setEnableOCSP(boolean enableOCSP)
+    {
+        checkNotStarted();
+
+        _enableOCSP = enableOCSP;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Location of the OCSP Responder
+     */
+    public String getOcspResponderURL()
+    {
+        return _ocspResponderURL;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set the location of the OCSP Responder.
+     * @param ocspResponderURL location of the OCSP Responder
+     */
+    public void setOcspResponderURL(String ocspResponderURL)
+    {
+        checkNotStarted();
+
+        _ocspResponderURL = ocspResponderURL;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set the key store.
+     * @param keyStore the key store to set
+     */
+    public void setKeyStore(KeyStore keyStore)
+    {
+        checkNotStarted();
+
+        _keyStore = keyStore;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set the trust store.
+     * @param trustStore the trust store to set
+     */
+    public void setTrustStore(KeyStore trustStore)
+    {
+        checkNotStarted();
+
+        _trustStore = trustStore;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set the key store resource.
+     * @param resource the key store resource to set
+     */
+    public void setKeyStoreResource(Resource resource)
+    {
+        checkNotStarted();
+
+        try
+        {
+            _keyStoreInputStream = resource.getInputStream();
+        }
+        catch (IOException e)
+        {
+             throw new InvalidParameterException("Unable to get resource "+
+                     "input stream for resource "+resource.toString());
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set the trust store resource.
+     * @param resource the trust store resource to set
+     */
+    public void setTrustStoreResource(Resource resource)
+    {
+        checkNotStarted();
+
+        try
+        {
+            _trustStoreInputStream = resource.getInputStream();
+        }
+        catch (IOException e)
+        {
+             throw new InvalidParameterException("Unable to get resource "+
+                     "input stream for resource "+resource.toString());
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+    * @return true if SSL Session caching is enabled
+    */
+    public boolean isSessionCachingEnabled()
+    {
+        return _sessionCachingEnabled;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set the flag to enable SSL Session caching.
+    * @param enableSessionCaching the value of the flag
+    */
+    public void setSessionCachingEnabled(boolean enableSessionCaching)
+    {
+        _sessionCachingEnabled = enableSessionCaching;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Get SSL session cache size.
+     * @return SSL session cache size
+     */
+    public int getSslSessionCacheSize()
+    {
+        return _sslSessionCacheSize;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** SEt SSL session cache size.
+     * @param sslSessionCacheSize SSL session cache size to set
+     */
+    public void setSslSessionCacheSize(int sslSessionCacheSize)
+    {
+        _sslSessionCacheSize = sslSessionCacheSize;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Get SSL session timeout.
+     * @return SSL session timeout
+     */
+    public int getSslSessionTimeout()
+    {
+        return _sslSessionTimeout;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set SSL session timeout.
+     * @param sslSessionTimeout SSL session timeout to set
+     */
+    public void setSslSessionTimeout(int sslSessionTimeout)
+    {
+        _sslSessionTimeout = sslSessionTimeout;
+    }
+
+
+    /* ------------------------------------------------------------ */
+    public SSLServerSocket newSslServerSocket(String host,int port,int backlog) throws IOException
+    {
+        SSLServerSocketFactory factory = _context.getServerSocketFactory();
+
+        SSLServerSocket socket =
+            (SSLServerSocket) (host==null ?
+                        factory.createServerSocket(port,backlog):
+                        factory.createServerSocket(port,backlog,InetAddress.getByName(host)));
+
+        if (getWantClientAuth())
+            socket.setWantClientAuth(getWantClientAuth());
+        if (getNeedClientAuth())
+            socket.setNeedClientAuth(getNeedClientAuth());
+
+        socket.setEnabledCipherSuites(selectCipherSuites(
+                                            socket.getEnabledCipherSuites(),
+                                            socket.getSupportedCipherSuites()));
+        socket.setEnabledProtocols(selectProtocols(socket.getEnabledProtocols(),socket.getSupportedProtocols()));
+
+        return socket;
+    }
+
+    /* ------------------------------------------------------------ */
+    public SSLSocket newSslSocket() throws IOException
+    {
+        SSLSocketFactory factory = _context.getSocketFactory();
+
+        SSLSocket socket = (SSLSocket)factory.createSocket();
+
+        if (getWantClientAuth())
+            socket.setWantClientAuth(getWantClientAuth());
+        if (getNeedClientAuth())
+            socket.setNeedClientAuth(getNeedClientAuth());
+
+        socket.setEnabledCipherSuites(selectCipherSuites(
+                                            socket.getEnabledCipherSuites(),
+                                            socket.getSupportedCipherSuites()));
+        socket.setEnabledProtocols(selectProtocols(socket.getEnabledProtocols(),socket.getSupportedProtocols()));
+
+        return socket;
+    }
+
+    /* ------------------------------------------------------------ */
+    public SSLEngine newSslEngine(String host,int port)
+    {
+        SSLEngine sslEngine=isSessionCachingEnabled()
+            ?_context.createSSLEngine(host, port)
+            :_context.createSSLEngine();
+
+        customize(sslEngine);
+        return sslEngine;
+    }
+
+    /* ------------------------------------------------------------ */
+    public SSLEngine newSslEngine()
+    {
+        SSLEngine sslEngine=_context.createSSLEngine();
+        customize(sslEngine);
+        return sslEngine;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void customize(SSLEngine sslEngine)
+    {
+        if (getWantClientAuth())
+            sslEngine.setWantClientAuth(getWantClientAuth());
+        if (getNeedClientAuth())
+            sslEngine.setNeedClientAuth(getNeedClientAuth());
+
+        sslEngine.setEnabledCipherSuites(selectCipherSuites(
+                sslEngine.getEnabledCipherSuites(),
+                sslEngine.getSupportedCipherSuites()));
+
+        sslEngine.setEnabledProtocols(selectProtocols(sslEngine.getEnabledProtocols(),sslEngine.getSupportedProtocols()));
+    }
+
+    /* ------------------------------------------------------------ */
+    public String toString()
+    {
+        return String.format("%s@%x(%s,%s)",
+                getClass().getSimpleName(),
+                hashCode(),
+                _keyStorePath,
+                _trustStorePath);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/statistic/CounterStatistic.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,117 @@
+//
+//  ========================================================================
+//  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.util.statistic;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.eclipse.jetty.util.Atomics;
+
+
+/* ------------------------------------------------------------ */
+/** Statistics on a counter value.
+ * <p>
+ * Keep total, current and maximum values of a counter that
+ * can be incremented and decremented. The total refers only
+ * to increments.
+ *
+ */
+public class CounterStatistic
+{
+    protected final AtomicLong _max = new AtomicLong();
+    protected final AtomicLong _curr = new AtomicLong();
+    protected final AtomicLong _total = new AtomicLong();
+
+    /* ------------------------------------------------------------ */
+    public void reset()
+    {
+        reset(0);
+    }
+
+    /* ------------------------------------------------------------ */
+    public void reset(final long value)
+    {
+        _max.set(value);
+        _curr.set(value);
+        _total.set(0); // total always set to 0 to properly calculate cumulative total
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param delta the amount to add to the count
+     */
+    public void add(final long delta)
+    {
+        long value=_curr.addAndGet(delta);
+        if (delta > 0)
+            _total.addAndGet(delta);
+        Atomics.updateMax(_max,value);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param delta the amount to subtract the count by.
+     */
+    public void subtract(final long delta)
+    {
+        add(-delta);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     */
+    public void increment()
+    {
+        add(1);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     */
+    public void decrement()
+    {
+        add(-1);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return max value
+     */
+    public long getMax()
+    {
+        return _max.get();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return current value
+     */
+    public long getCurrent()
+    {
+        return _curr.get();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return total value
+     */
+    public long getTotal()
+    {
+        return _total.get();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/statistic/SampleStatistic.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,109 @@
+//
+//  ========================================================================
+//  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.util.statistic;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.eclipse.jetty.util.Atomics;
+
+
+/* ------------------------------------------------------------ */
+/**
+ * SampledStatistics
+ * <p>
+ * Provides max, total, mean, count, variance, and standard
+ * deviation of continuous sequence of samples.
+ * <p>
+ * Calculates estimates of mean, variance, and standard deviation
+ * characteristics of a sample using a non synchronized
+ * approximation of the on-line algorithm presented
+ * in Donald Knuth's Art of Computer Programming, Volume 2,
+ * Seminumerical Algorithms, 3rd edition, page 232,
+ * Boston: Addison-Wesley. that cites a 1962 paper by B.P. Welford
+ * that can be found by following the link http://www.jstor.org/pss/1266577
+ * <p>
+ * This algorithm is also described in Wikipedia at
+ * http://en.wikipedia.org/w/index.php?title=Algorithms_for_calculating_variance&section=4#On-line_algorithm
+ */
+public class SampleStatistic
+{
+    protected final AtomicLong _max = new AtomicLong();
+    protected final AtomicLong _total = new AtomicLong();
+    protected final AtomicLong _count = new AtomicLong();
+    protected final AtomicLong _totalVariance100 = new AtomicLong();
+
+    public void reset()
+    {
+        _max.set(0);
+        _total.set(0);
+        _count.set(0);
+        _totalVariance100.set(0);
+    }
+
+    public void set(final long sample)
+    {
+        long total = _total.addAndGet(sample);
+        long count = _count.incrementAndGet();
+
+        if (count>1)
+        {
+            long mean10 = total*10/count;
+            long delta10 = sample*10 - mean10;
+            _totalVariance100.addAndGet(delta10*delta10);
+        }
+
+        Atomics.updateMax(_max, sample);
+    }
+
+    /**
+     * @return the max value
+     */
+    public long getMax()
+    {
+        return _max.get();
+    }
+
+    public long getTotal()
+    {
+        return _total.get();
+    }
+
+    public long getCount()
+    {
+        return _count.get();
+    }
+
+    public double getMean()
+    {
+        return (double)_total.get()/_count.get();
+    }
+
+    public double getVariance()
+    {
+        final long variance100 = _totalVariance100.get();
+        final long count = _count.get();
+
+        return count>1?((double)variance100)/100.0/(count-1):0.0;
+    }
+
+    public double getStdDev()
+    {
+        return Math.sqrt(getVariance());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/thread/ExecutorThreadPool.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,184 @@
+//
+//  ========================================================================
+//  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.util.thread;
+
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.util.component.AbstractLifeCycle;
+import org.eclipse.jetty.util.component.LifeCycle;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/* ------------------------------------------------------------ */
+/**
+ * Jetty ThreadPool using java 5 ThreadPoolExecutor
+ * This class wraps a {@link ExecutorService} as a {@link ThreadPool} and
+ * {@link LifeCycle} interfaces so that it may be used by the Jetty <code>org.eclipse.jetty.server.Server</code>
+ */
+public class ExecutorThreadPool extends AbstractLifeCycle implements ThreadPool, LifeCycle
+{
+    private static final Logger LOG = Log.getLogger(ExecutorThreadPool.class);
+    private final ExecutorService _executor;
+
+    /* ------------------------------------------------------------ */
+    public ExecutorThreadPool(ExecutorService executor)
+    {
+        _executor = executor;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Wraps an {@link ThreadPoolExecutor}.
+     * Max pool size is 256, pool thread timeout after 60 seconds and
+     * an unbounded {@link LinkedBlockingQueue} is used for the job queue;
+     */
+    public ExecutorThreadPool()
+    {
+        // Using an unbounded queue makes the maxThreads parameter useless
+        // Refer to ThreadPoolExecutor javadocs for details
+        this(new ThreadPoolExecutor(256, 256, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()));
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Wraps an {@link ThreadPoolExecutor}.
+     * Max pool size is 256, pool thread timeout after 60 seconds, and core pool size is 32 when queueSize >= 0.
+     * @param queueSize can be -1 for using an unbounded {@link LinkedBlockingQueue}, 0 for using a
+     * {@link SynchronousQueue}, greater than 0 for using a {@link ArrayBlockingQueue} of the given size.
+     */
+    public ExecutorThreadPool(int queueSize)
+    {
+        this(queueSize < 0 ? new ThreadPoolExecutor(256, 256, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()) :
+                queueSize == 0 ? new ThreadPoolExecutor(32, 256, 60, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()) :
+                        new ThreadPoolExecutor(32, 256, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(queueSize)));
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Wraps an {@link ThreadPoolExecutor} using
+     * an unbounded {@link LinkedBlockingQueue} is used for the jobs queue;
+     * @param corePoolSize must be equal to maximumPoolSize
+     * @param maximumPoolSize the maximum number of threads to allow in the pool
+     * @param keepAliveTime the max time a thread can remain idle, in milliseconds
+     */
+    public ExecutorThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime)
+    {
+        this(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.MILLISECONDS);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Wraps an {@link ThreadPoolExecutor} using
+     * an unbounded {@link LinkedBlockingQueue} is used for the jobs queue.
+     * @param corePoolSize must be equal to maximumPoolSize
+     * @param maximumPoolSize the maximum number of threads to allow in the pool
+     * @param keepAliveTime the max time a thread can remain idle
+     * @param unit the unit for the keepAliveTime
+     */
+    public ExecutorThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit)
+    {
+        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, new LinkedBlockingQueue<Runnable>());
+    }
+
+    /* ------------------------------------------------------------ */
+
+    /**
+     * Wraps an {@link ThreadPoolExecutor}
+     * @param corePoolSize the number of threads to keep in the pool, even if they are idle
+     * @param maximumPoolSize the maximum number of threads to allow in the pool
+     * @param keepAliveTime the max time a thread can remain idle
+     * @param unit the unit for the keepAliveTime
+     * @param workQueue the queue to use for holding tasks before they are executed
+     */
+    public ExecutorThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)
+    {
+        this(new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue));
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean dispatch(Runnable job)
+    {
+        try
+        {
+            _executor.execute(job);
+            return true;
+        }
+        catch(RejectedExecutionException e)
+        {
+            LOG.warn(e);
+            return false;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public int getIdleThreads()
+    {
+        if (_executor instanceof ThreadPoolExecutor)
+        {
+            final ThreadPoolExecutor tpe = (ThreadPoolExecutor)_executor;
+            return tpe.getPoolSize() - tpe.getActiveCount();
+        }
+        return -1;
+    }
+
+    /* ------------------------------------------------------------ */
+    public int getThreads()
+    {
+        if (_executor instanceof ThreadPoolExecutor)
+        {
+            final ThreadPoolExecutor tpe = (ThreadPoolExecutor)_executor;
+            return tpe.getPoolSize();
+        }
+        return -1;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isLowOnThreads()
+    {
+        if (_executor instanceof ThreadPoolExecutor)
+        {
+            final ThreadPoolExecutor tpe = (ThreadPoolExecutor)_executor;
+            // getActiveCount() locks the thread pool, so execute it last
+            return tpe.getPoolSize() == tpe.getMaximumPoolSize() &&
+                    tpe.getQueue().size() >= tpe.getPoolSize() - tpe.getActiveCount();
+        }
+        return false;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void join() throws InterruptedException
+    {
+        _executor.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    protected void doStop() throws Exception
+    {
+        super.doStop();
+        _executor.shutdownNow();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/thread/QueuedThreadPool.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,678 @@
+//
+//  ========================================================================
+//  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.util.thread;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.eclipse.jetty.util.BlockingArrayQueue;
+import org.eclipse.jetty.util.component.AbstractLifeCycle;
+import org.eclipse.jetty.util.component.AggregateLifeCycle;
+import org.eclipse.jetty.util.component.Dumpable;
+import org.eclipse.jetty.util.component.LifeCycle;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.ThreadPool.SizedThreadPool;
+
+public class QueuedThreadPool extends AbstractLifeCycle implements SizedThreadPool, Executor, Dumpable
+{
+    private static final Logger LOG = Log.getLogger(QueuedThreadPool.class);
+
+    private final AtomicInteger _threadsStarted = new AtomicInteger();
+    private final AtomicInteger _threadsIdle = new AtomicInteger();
+    private final AtomicLong _lastShrink = new AtomicLong();
+    private final ConcurrentLinkedQueue<Thread> _threads=new ConcurrentLinkedQueue<Thread>();
+    private final Object _joinLock = new Object();
+    private BlockingQueue<Runnable> _jobs;
+    private String _name;
+    private int _maxIdleTimeMs=60000;
+    private int _maxThreads=254;
+    private int _minThreads=8;
+    private int _maxQueued=-1;
+    private int _priority=Thread.NORM_PRIORITY;
+    private boolean _daemon=false;
+    private int _maxStopTime=100;
+    private boolean _detailedDump=false;
+
+    /* ------------------------------------------------------------------- */
+    /** Construct
+     */
+    public QueuedThreadPool()
+    {
+        _name="qtp"+super.hashCode();
+    }
+
+    /* ------------------------------------------------------------------- */
+    /** Construct
+     */
+    public QueuedThreadPool(int maxThreads)
+    {
+        this();
+        setMaxThreads(maxThreads);
+    }
+
+    /* ------------------------------------------------------------------- */
+    /** Construct
+     */
+    public QueuedThreadPool(BlockingQueue<Runnable> jobQ)
+    {
+        this();
+        _jobs=jobQ;
+        _jobs.clear();
+    }
+
+
+    /* ------------------------------------------------------------ */
+    @Override
+    protected void doStart() throws Exception
+    {
+        super.doStart();
+        _threadsStarted.set(0);
+
+        if (_jobs==null)
+        {
+            _jobs=_maxQueued>0 ?new ArrayBlockingQueue<Runnable>(_maxQueued)
+                :new BlockingArrayQueue<Runnable>(_minThreads,_minThreads);
+        }
+
+        int threads=_threadsStarted.get();
+        while (isRunning() && threads<_minThreads)
+        {
+            startThread(threads);
+            threads=_threadsStarted.get();
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    protected void doStop() throws Exception
+    {
+        super.doStop();
+        long start=System.currentTimeMillis();
+
+        // let jobs complete naturally for a while
+        while (_threadsStarted.get()>0 && (System.currentTimeMillis()-start) < (_maxStopTime/2))
+            Thread.sleep(1);
+
+        // kill queued jobs and flush out idle jobs
+        _jobs.clear();
+        Runnable noop = new Runnable(){public void run(){}};
+        for  (int i=_threadsIdle.get();i-->0;)
+            _jobs.offer(noop);
+        Thread.yield();
+
+        // interrupt remaining threads
+        if (_threadsStarted.get()>0)
+            for (Thread thread : _threads)
+                thread.interrupt();
+
+        // wait for remaining threads to die
+        while (_threadsStarted.get()>0 && (System.currentTimeMillis()-start) < _maxStopTime)
+        {
+            Thread.sleep(1);
+        }
+        Thread.yield();
+        int size=_threads.size();
+        if (size>0)
+        {
+            LOG.warn(size+" threads could not be stopped");
+
+            if (size==1 || LOG.isDebugEnabled())
+            {
+                for (Thread unstopped : _threads)
+                {
+                    LOG.info("Couldn't stop "+unstopped);
+                    for (StackTraceElement element : unstopped.getStackTrace())
+                    {
+                        LOG.info(" at "+element);
+                    }
+                }
+            }
+        }
+
+        synchronized (_joinLock)
+        {
+            _joinLock.notifyAll();
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Delegated to the named or anonymous Pool.
+     */
+    public void setDaemon(boolean daemon)
+    {
+        _daemon=daemon;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set the maximum thread idle time.
+     * Threads that are idle for longer than this period may be
+     * stopped.
+     * Delegated to the named or anonymous Pool.
+     * @see #getMaxIdleTimeMs
+     * @param maxIdleTimeMs Max idle time in ms.
+     */
+    public void setMaxIdleTimeMs(int maxIdleTimeMs)
+    {
+        _maxIdleTimeMs=maxIdleTimeMs;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param stopTimeMs maximum total time that stop() will wait for threads to die.
+     */
+    public void setMaxStopTimeMs(int stopTimeMs)
+    {
+        _maxStopTime = stopTimeMs;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set the maximum number of threads.
+     * Delegated to the named or anonymous Pool.
+     * @see #getMaxThreads
+     * @param maxThreads maximum number of threads.
+     */
+    public void setMaxThreads(int maxThreads)
+    {
+        _maxThreads=maxThreads;
+        if (_minThreads>_maxThreads)
+            _minThreads=_maxThreads;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set the minimum number of threads.
+     * Delegated to the named or anonymous Pool.
+     * @see #getMinThreads
+     * @param minThreads minimum number of threads
+     */
+    public void setMinThreads(int minThreads)
+    {
+        _minThreads=minThreads;
+
+        if (_minThreads>_maxThreads)
+            _maxThreads=_minThreads;
+
+        int threads=_threadsStarted.get();
+        while (isStarted() && threads<_minThreads)
+        {
+            startThread(threads);
+            threads=_threadsStarted.get();
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param name Name of the BoundedThreadPool to use when naming Threads.
+     */
+    public void setName(String name)
+    {
+        if (isRunning())
+            throw new IllegalStateException("started");
+        _name= name;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set the priority of the pool threads.
+     *  @param priority the new thread priority.
+     */
+    public void setThreadsPriority(int priority)
+    {
+        _priority=priority;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return maximum queue size
+     */
+    public int getMaxQueued()
+    {
+        return _maxQueued;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param max job queue size
+     */
+    public void setMaxQueued(int max)
+    {
+        if (isRunning())
+            throw new IllegalStateException("started");
+        _maxQueued=max;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Get the maximum thread idle time.
+     * Delegated to the named or anonymous Pool.
+     * @see #setMaxIdleTimeMs
+     * @return Max idle time in ms.
+     */
+    public int getMaxIdleTimeMs()
+    {
+        return _maxIdleTimeMs;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return maximum total time that stop() will wait for threads to die.
+     */
+    public int getMaxStopTimeMs()
+    {
+        return _maxStopTime;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Set the maximum number of threads.
+     * Delegated to the named or anonymous Pool.
+     * @see #setMaxThreads
+     * @return maximum number of threads.
+     */
+    public int getMaxThreads()
+    {
+        return _maxThreads;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Get the minimum number of threads.
+     * Delegated to the named or anonymous Pool.
+     * @see #setMinThreads
+     * @return minimum number of threads.
+     */
+    public int getMinThreads()
+    {
+        return _minThreads;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The name of the BoundedThreadPool.
+     */
+    public String getName()
+    {
+        return _name;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Get the priority of the pool threads.
+     *  @return the priority of the pool threads.
+     */
+    public int getThreadsPriority()
+    {
+        return _priority;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Delegated to the named or anonymous Pool.
+     */
+    public boolean isDaemon()
+    {
+        return _daemon;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isDetailedDump()
+    {
+        return _detailedDump;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setDetailedDump(boolean detailedDump)
+    {
+        _detailedDump = detailedDump;
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean dispatch(Runnable job)
+    {
+        if (isRunning())
+        {
+            final int jobQ = _jobs.size();
+            final int idle = getIdleThreads();
+            if(_jobs.offer(job))
+            {
+                // If we had no idle threads or the jobQ is greater than the idle threads
+                if (idle==0 || jobQ>idle)
+                {
+                    int threads=_threadsStarted.get();
+                    if (threads<_maxThreads)
+                        startThread(threads);
+                }
+                return true;
+            }
+        }
+        LOG.debug("Dispatched {} to stopped {}",job,this);
+        return false;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void execute(Runnable job)
+    {
+        if (!dispatch(job))
+            throw new RejectedExecutionException();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Blocks until the thread pool is {@link LifeCycle#stop stopped}.
+     */
+    public void join() throws InterruptedException
+    {
+        synchronized (_joinLock)
+        {
+            while (isRunning())
+                _joinLock.wait();
+        }
+
+        while (isStopping())
+            Thread.sleep(1);
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The total number of threads currently in the pool
+     */
+    public int getThreads()
+    {
+        return _threadsStarted.get();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The number of idle threads in the pool
+     */
+    public int getIdleThreads()
+    {
+        return _threadsIdle.get();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return True if the pool is at maxThreads and there are not more idle threads than queued jobs
+     */
+    public boolean isLowOnThreads()
+    {
+        return _threadsStarted.get()==_maxThreads && _jobs.size()>=_threadsIdle.get();
+    }
+
+    /* ------------------------------------------------------------ */
+    private boolean startThread(int threads)
+    {
+        final int next=threads+1;
+        if (!_threadsStarted.compareAndSet(threads,next))
+            return false;
+
+        boolean started=false;
+        try
+        {
+            Thread thread=newThread(_runnable);
+            thread.setDaemon(_daemon);
+            thread.setPriority(_priority);
+            thread.setName(_name+"-"+thread.getId());
+            _threads.add(thread);
+
+            thread.start();
+            started=true;
+        }
+        finally
+        {
+            if (!started)
+                _threadsStarted.decrementAndGet();
+        }
+        return started;
+    }
+
+    /* ------------------------------------------------------------ */
+    protected Thread newThread(Runnable runnable)
+    {
+        return new Thread(runnable);
+    }
+
+
+    /* ------------------------------------------------------------ */
+    public String dump()
+    {
+        return AggregateLifeCycle.dump(this);
+    }
+
+    /* ------------------------------------------------------------ */
+    public void dump(Appendable out, String indent) throws IOException
+    {
+        List<Object> dump = new ArrayList<Object>(getMaxThreads());
+        for (final Thread thread: _threads)
+        {
+            final StackTraceElement[] trace=thread.getStackTrace();
+            boolean inIdleJobPoll=false;
+            // trace can be null on early java 6 jvms
+            if (trace != null)
+            {
+                for (StackTraceElement t : trace)
+                {
+                    if ("idleJobPoll".equals(t.getMethodName()))
+                    {
+                        inIdleJobPoll = true;
+                        break;
+                    }
+                }
+            }
+            final boolean idle=inIdleJobPoll;
+
+            if (_detailedDump)
+            {
+                dump.add(new Dumpable()
+                {
+                    public void dump(Appendable out, String indent) throws IOException
+                    {
+                        out.append(String.valueOf(thread.getId())).append(' ').append(thread.getName()).append(' ').append(thread.getState().toString()).append(idle?" IDLE":"").append('\n');
+                        if (!idle)
+                            AggregateLifeCycle.dump(out,indent,Arrays.asList(trace));
+                    }
+
+                    public String dump()
+                    {
+                        return null;
+                    }
+                });
+            }
+            else
+            {
+                dump.add(thread.getId()+" "+thread.getName()+" "+thread.getState()+" @ "+(trace.length>0?trace[0]:"???")+(idle?" IDLE":""));
+            }
+        }
+
+        AggregateLifeCycle.dumpObject(out,this);
+        AggregateLifeCycle.dump(out,indent,dump);
+
+    }
+
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public String toString()
+    {
+        return _name+"{"+getMinThreads()+"<="+getIdleThreads()+"<="+getThreads()+"/"+getMaxThreads()+","+(_jobs==null?-1:_jobs.size())+"}";
+    }
+
+    /* ------------------------------------------------------------ */
+    private Runnable idleJobPoll() throws InterruptedException
+    {
+        return _jobs.poll(_maxIdleTimeMs,TimeUnit.MILLISECONDS);
+    }
+
+    /* ------------------------------------------------------------ */
+    private Runnable _runnable = new Runnable()
+    {
+        public void run()
+        {
+            boolean shrink=false;
+            try
+            {
+                Runnable job=_jobs.poll();
+                while (isRunning())
+                {
+                    // Job loop
+                    while (job!=null && isRunning())
+                    {
+                        runJob(job);
+                        job=_jobs.poll();
+                    }
+
+                    // Idle loop
+                    try
+                    {
+                        _threadsIdle.incrementAndGet();
+
+                        while (isRunning() && job==null)
+                        {
+                            if (_maxIdleTimeMs<=0)
+                                job=_jobs.take();
+                            else
+                            {
+                                // maybe we should shrink?
+                                final int size=_threadsStarted.get();
+                                if (size>_minThreads)
+                                {
+                                    long last=_lastShrink.get();
+                                    long now=System.currentTimeMillis();
+                                    if (last==0 || (now-last)>_maxIdleTimeMs)
+                                    {
+                                        shrink=_lastShrink.compareAndSet(last,now) &&
+                                        _threadsStarted.compareAndSet(size,size-1);
+                                        if (shrink)
+                                            return;
+                                    }
+                                }
+                                job=idleJobPoll();
+                            }
+                        }
+                    }
+                    finally
+                    {
+                        _threadsIdle.decrementAndGet();
+                    }
+                }
+            }
+            catch(InterruptedException e)
+            {
+                LOG.ignore(e);
+            }
+            catch(Exception e)
+            {
+                LOG.warn(e);
+            }
+            finally
+            {
+                if (!shrink)
+                    _threadsStarted.decrementAndGet();
+                _threads.remove(Thread.currentThread());
+            }
+        }
+    };
+
+    /* ------------------------------------------------------------ */
+    /**
+     * <p>Runs the given job in the {@link Thread#currentThread() current thread}.</p>
+     * <p>Subclasses may override to perform pre/post actions before/after the job is run.</p>
+     *
+     * @param job the job to run
+     */
+    protected void runJob(Runnable job)
+    {
+        job.run();
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return the job queue
+     */
+    protected BlockingQueue<Runnable> getQueue()
+    {
+        return _jobs;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param id The thread ID to stop.
+     * @return true if the thread was found and stopped.
+     * @deprecated Use {@link #interruptThread(long)} in preference
+     */
+    @Deprecated
+    public boolean stopThread(long id)
+    {
+        for (Thread thread: _threads)
+        {
+            if (thread.getId()==id)
+            {
+                thread.stop();
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param id The thread ID to interrupt.
+     * @return true if the thread was found and interrupted.
+     */
+    public boolean interruptThread(long id)
+    {
+        for (Thread thread: _threads)
+        {
+            if (thread.getId()==id)
+            {
+                thread.interrupt();
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param id The thread ID to interrupt.
+     * @return true if the thread was found and interrupted.
+     */
+    public String dumpThread(long id)
+    {
+        for (Thread thread: _threads)
+        {
+            if (thread.getId()==id)
+            {
+                StringBuilder buf = new StringBuilder();
+                buf.append(thread.getId()).append(" ").append(thread.getName()).append(" ").append(thread.getState()).append(":\n");
+                for (StackTraceElement element : thread.getStackTrace())
+                    buf.append("  at ").append(element.toString()).append('\n');
+                return buf.toString();
+            }
+        }
+        return null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/thread/ShutdownThread.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,148 @@
+//
+//  ========================================================================
+//  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.util.thread;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.eclipse.jetty.util.component.Destroyable;
+import org.eclipse.jetty.util.component.LifeCycle;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+
+/* ------------------------------------------------------------ */
+/**
+ * ShutdownThread is a shutdown hook thread implemented as 
+ * singleton that maintains a list of lifecycle instances
+ * that are registered with it and provides ability to stop
+ * these lifecycles upon shutdown of the Java Virtual Machine 
+ */
+public class ShutdownThread extends Thread
+{
+    private static final Logger LOG = Log.getLogger(ShutdownThread.class);
+    private static final ShutdownThread _thread = new ShutdownThread();
+
+    private boolean _hooked;
+    private final List<LifeCycle> _lifeCycles = new CopyOnWriteArrayList<LifeCycle>();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Default constructor for the singleton
+     * 
+     * Registers the instance as shutdown hook with the Java Runtime
+     */
+    private ShutdownThread()
+    {
+    }
+    
+    /* ------------------------------------------------------------ */
+    private synchronized void hook()
+    {
+        try
+        {
+            if (!_hooked)
+                Runtime.getRuntime().addShutdownHook(this);
+            _hooked=true;
+        }
+        catch(Exception e)
+        {
+            LOG.ignore(e);
+            LOG.info("shutdown already commenced");
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    private synchronized void unhook()
+    {
+        try
+        {
+            _hooked=false;
+            Runtime.getRuntime().removeShutdownHook(this);
+        }
+        catch(Exception e)
+        {
+            LOG.ignore(e);
+            LOG.debug("shutdown already commenced");
+        }
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * Returns the instance of the singleton
+     * 
+     * @return the singleton instance of the {@link ShutdownThread}
+     */
+    public static ShutdownThread getInstance()
+    {
+        return _thread;
+    }
+
+    /* ------------------------------------------------------------ */
+    public static synchronized void register(LifeCycle... lifeCycles)
+    {
+        _thread._lifeCycles.addAll(Arrays.asList(lifeCycles));
+        if (_thread._lifeCycles.size()>0)
+            _thread.hook();
+    }
+
+    /* ------------------------------------------------------------ */
+    public static synchronized void register(int index, LifeCycle... lifeCycles)
+    {
+        _thread._lifeCycles.addAll(index,Arrays.asList(lifeCycles));
+        if (_thread._lifeCycles.size()>0)
+            _thread.hook();
+    }
+    
+    /* ------------------------------------------------------------ */
+    public static synchronized void deregister(LifeCycle lifeCycle)
+    {
+        _thread._lifeCycles.remove(lifeCycle);
+        if (_thread._lifeCycles.size()==0)
+            _thread.unhook();
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public void run()
+    {
+        for (LifeCycle lifeCycle : _thread._lifeCycles)
+        {
+            try
+            {
+                if (lifeCycle.isStarted())
+                {
+                    lifeCycle.stop();
+                    LOG.debug("Stopped {}",lifeCycle);
+                }
+                
+                if (lifeCycle instanceof Destroyable)
+                {
+                    ((Destroyable)lifeCycle).destroy();
+                    LOG.debug("Destroyed {}",lifeCycle);
+                }
+            }
+            catch (Exception ex)
+            {
+                LOG.debug(ex);
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/thread/ThreadPool.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,67 @@
+//
+//  ========================================================================
+//  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.util.thread;
+
+import org.eclipse.jetty.util.component.LifeCycle;
+
+/* ------------------------------------------------------------ */
+/** ThreadPool.
+ * 
+ *
+ */
+public interface ThreadPool
+{
+    /* ------------------------------------------------------------ */
+    public abstract boolean dispatch(Runnable job);
+
+    /* ------------------------------------------------------------ */
+    /**
+     * Blocks until the thread pool is {@link LifeCycle#stop stopped}.
+     */
+    public void join() throws InterruptedException;
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The total number of threads currently in the pool
+     */
+    public int getThreads();
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return The number of idle threads in the pool
+     */
+    public int getIdleThreads();
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @return True if the pool is low on threads
+     */
+    public boolean isLowOnThreads();
+    
+
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    public interface SizedThreadPool extends ThreadPool
+    {
+        public int getMinThreads();
+        public int getMaxThreads();
+        public void setMinThreads(int threads);
+        public void setMaxThreads(int threads);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/thread/Timeout.java	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,380 @@
+//
+//  ========================================================================
+//  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.util.thread;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+
+/* ------------------------------------------------------------ */
+/** Timeout queue.
+ * This class implements a timeout queue for timers that are at least as likely to be cancelled as they are to expire.
+ * Unlike the util timeout class, the duration of the timeouts is shared by all scheduled tasks and if the duration 
+ * is changed, this affects all scheduled tasks.
+ * <p>
+ * The nested class Task should be extended by users of this class to obtain call back notification of 
+ * expires. 
+ */
+public class Timeout
+{
+    private static final Logger LOG = Log.getLogger(Timeout.class);
+    private Object _lock;
+    private long _duration;
+    private volatile long _now=System.currentTimeMillis();
+    private Task _head=new Task();
+
+    /* ------------------------------------------------------------ */
+    public Timeout()
+    {
+        _lock=new Object();
+        _head._timeout=this;
+    }
+
+    /* ------------------------------------------------------------ */
+    public Timeout(Object lock)
+    {
+        _lock=lock;
+        _head._timeout=this;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @return Returns the duration.
+     */
+    public long getDuration()
+    {
+        return _duration;
+    }
+
+    /* ------------------------------------------------------------ */
+    /**
+     * @param duration The duration to set.
+     */
+    public void setDuration(long duration)
+    {
+        _duration = duration;
+    }
+
+    /* ------------------------------------------------------------ */
+    public long setNow()
+    {
+        return _now=System.currentTimeMillis();
+    }
+    
+    /* ------------------------------------------------------------ */
+    public long getNow()
+    {
+        return _now;
+    }
+
+    /* ------------------------------------------------------------ */
+    public void setNow(long now)
+    {
+        _now=now;
+    }
+
+    /* ------------------------------------------------------------ */
+    /** Get an expired tasks.
+     * This is called instead of {@link #tick()} to obtain the next
+     * expired Task, but without calling it's {@link Task#expire()} or
+     * {@link Task#expired()} methods.
+     * 
+     * @return the next expired task or null.
+     */
+    public Task expired()
+    {
+        synchronized (_lock)
+        {
+            long _expiry = _now-_duration;
+
+            if (_head._next!=_head)
+            {
+                Task task = _head._next;
+                if (task._timestamp>_expiry)
+                    return null;
+
+                task.unlink();
+                task._expired=true;
+                return task;
+            }
+            return null;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public void tick()
+    {
+        final long expiry = _now-_duration;
+
+        Task task=null;
+        while (true)
+        {
+            try
+            {
+                synchronized (_lock)
+                {
+                    task= _head._next;
+                    if (task==_head || task._timestamp>expiry)
+                        break;
+                    task.unlink();
+                    task._expired=true;
+                    task.expire();
+                }
+                
+                task.expired();
+            }
+            catch(Throwable th)
+            {
+                LOG.warn(Log.EXCEPTION,th);
+            }
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public void tick(long now)
+    {
+        _now=now;
+        tick();
+    }
+
+    /* ------------------------------------------------------------ */
+    public void schedule(Task task)
+    {
+        schedule(task,0L);
+    }
+    
+    /* ------------------------------------------------------------ */
+    /**
+     * @param task
+     * @param delay A delay in addition to the default duration of the timeout
+     */
+    public void schedule(Task task,long delay)
+    {
+        synchronized (_lock)
+        {
+            if (task._timestamp!=0)
+            {
+                task.unlink();
+                task._timestamp=0;
+            }
+            task._timeout=this;
+            task._expired=false;
+            task._delay=delay;
+            task._timestamp = _now+delay;
+
+            Task last=_head._prev;
+            while (last!=_head)
+            {
+                if (last._timestamp <= task._timestamp)
+                    break;
+                last=last._prev;
+            }
+            last.link(task);
+        }
+    }
+
+
+    /* ------------------------------------------------------------ */
+    public void cancelAll()
+    {
+        synchronized (_lock)
+        {
+            _head._next=_head._prev=_head;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public boolean isEmpty()
+    {
+        synchronized (_lock)
+        {
+            return _head._next==_head;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    public long getTimeToNext()
+    {
+        synchronized (_lock)
+        {
+            if (_head._next==_head)
+                return -1;
+            long to_next = _duration+_head._next._timestamp-_now;
+            return to_next<0?0:to_next;
+        }
+    }
+
+    /* ------------------------------------------------------------ */
+    @Override
+    public String toString()
+    {
+        StringBuffer buf = new StringBuffer();
+        buf.append(super.toString());
+        
+        Task task = _head._next;
+        while (task!=_head)
+        {
+            buf.append("-->");
+            buf.append(task);
+            task=task._next;
+        }
+        
+        return buf.toString();
+    }
+
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    /* ------------------------------------------------------------ */
+    /** Task.
+     * The base class for scheduled timeouts.  This class should be
+     * extended to implement the expire() method, which is called if the
+     * timeout expires.
+     * 
+     * 
+     *
+     */
+    public static class Task
+    {
+        Task _next;
+        Task _prev;
+        Timeout _timeout;
+        long _delay;
+        long _timestamp=0;
+        boolean _expired=false;
+
+        /* ------------------------------------------------------------ */
+        protected Task()
+        {
+            _next=_prev=this;
+        }
+
+        /* ------------------------------------------------------------ */
+        public long getTimestamp()
+        {
+            return _timestamp;
+        }
+
+        /* ------------------------------------------------------------ */
+        public long getAge()
+        {
+            final Timeout t = _timeout;
+            if (t!=null)
+            {
+                final long now=t._now;
+                if (now!=0 && _timestamp!=0)
+                    return now-_timestamp;
+            }
+            return 0;
+        }
+
+        /* ------------------------------------------------------------ */
+        private void unlink()
+        {
+            _next._prev=_prev;
+            _prev._next=_next;
+            _next=_prev=this;
+            _expired=false;
+        }
+
+        /* ------------------------------------------------------------ */
+        private void link(Task task)
+        {
+            Task next_next = _next;
+            _next._prev=task;
+            _next=task;
+            _next._next=next_next;
+            _next._prev=this;   
+        }
+        
+        /* ------------------------------------------------------------ */
+        /** Schedule the task on the given timeout.
+         * The task exiry will be called after the timeout duration.
+         * @param timer
+         */
+        public void schedule(Timeout timer)
+        {
+            timer.schedule(this);
+        }
+        
+        /* ------------------------------------------------------------ */
+        /** Schedule the task on the given timeout.
+         * The task exiry will be called after the timeout duration.
+         * @param timer
+         */
+        public void schedule(Timeout timer, long delay)
+        {
+            timer.schedule(this,delay);
+        }
+        
+        /* ------------------------------------------------------------ */
+        /** Reschedule the task on the current timeout.
+         * The task timeout is rescheduled as if it had been cancelled and
+         * scheduled on the current timeout.
+         */
+        public void reschedule()
+        {
+            Timeout timeout = _timeout;
+            if (timeout!=null)
+                timeout.schedule(this,_delay);
+        }
+        
+        /* ------------------------------------------------------------ */
+        /** Cancel the task.
+         * Remove the task from the timeout.
+         */
+        public void cancel()
+        {
+            Timeout timeout = _timeout;
+            if (timeout!=null)
+            {
+                synchronized (timeout._lock)
+                {
+                    unlink();
+                    _timestamp=0;
+                }
+            }
+        }
+        
+        /* ------------------------------------------------------------ */
+        public boolean isExpired() { return _expired; }
+
+        /* ------------------------------------------------------------ */
+	public boolean isScheduled() { return _next!=this; }
+        
+        /* ------------------------------------------------------------ */
+        /** Expire task.
+         * This method is called when the timeout expires. It is called
+         * in the scope of the synchronize block (on this) that sets 
+         * the {@link #isExpired()} state to true.
+         * @see #expired() For an unsynchronized callback.
+         */
+        protected void expire(){}
+
+        /* ------------------------------------------------------------ */
+        /** Expire task.
+         * This method is called when the timeout expires. It is called 
+         * outside of any synchronization scope and may be delayed. 
+         * 
+         */
+        public void expired(){}
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/thread/jmx/QueuedThreadPool-mbean.properties	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,16 @@
+QueuedThreadPool: A thread pool with no max bound by default
+minThreads: Minimum number of threads in the pool
+maxThreads: Maximum number threads in the pool
+maxQueued: The maximum size of the job queue or -1 for unbounded.
+name: Name of the thread pool
+daemon: Is pool thread using daemon thread
+threadsPriority: The priority of threads in the pool
+maxIdleTimeMs: Maximum time a thread may be idle in ms
+detailedDump: Full stack detail in dump output
+dump(): Dump thread state
+stopThread(long): Stop a pool thread
+stopThread(long)[0]: id:Thread ID
+interruptThread(long): Interrupt a pool thread
+interruptThread(long)[0]: id:Thread ID
+dumpThread(long): Dump a pool thread stack
+dumpThread(long)[0]: id:Thread ID
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/eclipse/jetty/util/thread/jmx/ThreadPool-mbean.properties	Wed Sep 07 21:15:48 2016 -0600
@@ -0,0 +1,4 @@
+ThreadPool: Pool of threads
+threads: RO:Number of Threads in pool
+idleThreads: RO:Number of idle Threads in pool
+lowOnThreads: RO:Indicates the pool is low on available threads.